Tag Archives: javascript

Hello World 他山石

defineProperty

学习VUE“双向绑定”的实现,DOM变化时,同步给js变量,这一层比较容易理解,使用DOM的事件绑定就好了;但是当js变量发送变化时,是如何监控到、然后更新到DOM的,这点就比较有意思的。
看VUE代码,里面使用了ES5新加入的“Object.defineProperty()”,属性拦截器

看名字就比较好理解,使用defineProperty定义的属性,可以针对getter,和setter操作做监控、拦截,比如下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let o = {};
Object.defineProperty(o, 'b', {
  configurable: true,
  enumerable: true,
  get: function(){
    console.log('get run');     //log1
  },
  set: function(v){
    console.log('set run', v);  //log2
  }
});
 
setInterval(function(){
  o.b = new Date().toLocaleString();
  console.log('o.b, ', o.b);     //log3  undefined
}, 3000);

这样,每隔3秒,log2,o.b赋值时,log2,就会被打印出来
但是,下面的反应比较奇怪,getter也被拦截了,log3位置,打印出来的是“undefined”。。。
这是因为getter里没有返回值
但是如果你胆敢在getter“return this.b”,运行时就会死循环,因为如果this.b又会去调getger
(是的,这就是js。。。增加一个看似“美丽强大”的新特性的同时,还给你附加几个坑,等你往里跳。。。。。)
那么,还能不能让人好好使用这个拦截器哪?
当然可以,这里,我们可以加一个“中间变量”过渡,setter时,给中间变量赋值;getter时,return这个中间变量,就不会死循环了

当然,还可以借助经典的“function-Class”定义,对整个过程做封装,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function O1(){
  let v1;     //中间变量
 
  Object.defineProperty(this, 'v1', {
    get: function(){
      console.log('get run', v1);
      return v1;
    },
    set: function(v){
      v1 = v;
      console.log('set run', v);
    }
  });
}
 
var o1 = new O1();
 
setInterval(function(){
  o1.v1 = new Date().toLocaleString();
  console.log('o1.v1, ', o1.v1);
}, 3000);

———
转载请注明出处: http://www.jiangkunlun.com/2019/06/js_defineproperty

Hello World

js里,image onload事件无效问题的处理

使用image的src,做页面事件上报,上报成功,标记
但是奇怪的是,使用new Image()方式,注册的onload一直无效
使用前端开发工具,看img请求,返回,都正常
查了一下,有些人说,是onload,在src后面的问题,img如果加载很快,以至于在onload就加载完了,所以需要将onload放在src前面
。。。可以肯定的是,保证不是这个原因,因为js是事件驱动的,除非src和onload不在同一个代码段里
后来,突发奇想,测试onerror,居然会执行
于是翻出红宝书查,onload是在图片加载完成,并且图片解析无误时,才会触发。。。图片解析无误
。。。
这个请求,返回的是一段base64,很久以前的一段老代码,是一个1*1的图,但是,不知什么时候,估计是为了返回内容少一点,删了一段。。。出错了,图片解析不成功
于是重新搞了一个图片,echo出去~~ok了!!
。。。
重复一下上面一大段文字的中心意思:
onload是在图片加载完成,并且图片解析无误时,才会触发
———-
转载请注明出处:http://www.jiangkunlun.com/2018/04/image_onload_无效/

Hello World

来看一下不按常理出牌的js

某公众号上看了一篇“一道面试题引发的对JavaScript类型转换的思考”,里面提到的面试题是这样的:

实现一个函数,满足如下功能:

1
2
3
add(1)(2) // 3  
add(1, 2, 3)(10) // 16  
add(1)(2)(3)(4)(5) // 15

这么一个小功能,实现方法当然见仁见智,这里想说的js的function类型。
我面试前端的时候,经常问这么样一个问题:“jquery里的‘$’是个什么东西?”
对方不明白的话,就直接问“typeof $,会返回什么?”
能回答“function”的,就说明对方对js的数据类型是有了解的
对,$本身是个function,只是上面挂了各种属性和方法
这是第一点

第二点
是js的高阶函数
在学习闭包的时候,会用到高阶函数。所谓高阶函数,说白了就是一个函数,return的还是函数
上面的add(x)(x)(x),所以add肯定是高阶函数的形式

第三点
如果return的是函数,怎么得到相加后的数字
所以函数本身要带上toString(),或者valueOf()

好了,结合上面几点,下面是这个函数的实现,比预想的简单:

1
2
3
4
5
6
7
8
9
10
11
12
function add(){   //第一层add,第一次调用时执行
  var s0 = 0;     //闭包参数,记录累加值
  var f1 = function(){ //内层add,级联调用,实际上是调用的它
    for(var i = 0; i < arguments.length; i++) s0 += arguments[i];
    return f1;
  };
  f1.toString = function(){
    return s0;   //返回累加值
  }
  f1.apply(null, arguments);  //第一次执行时,先add一次
  return f1;
}

————-
转载请注明出处:昆仑的山头

Hello World

判断文本宽度有几种方法

很多时候,我们需要判断一个字符串扔到页面上显示时的宽度,这里,按照我自己的认知顺序说一下都有哪些判断方法

初级:str.length
这个不用说,大家都明白,最简单,也最不靠谱
但是有一个地方要注意:一个中文字符,js里的length是1,而php里,根据编码不同,可以是3或2

中级:正则判断支付类型,按不同的长度处理

1
2
3
4
5
6
7
8
9
10
11
  //判断字符串的长度,全角返回2,半角返回1
  var b2reg = /[\u0000-\u00ff|\uff61-\uff9f|\uffe8-\uffee]+$/;
  function byteLen(s){
    var c = 0;
    if(s && s.length > 0){
      var a = s.split("");
      for(i in a){
        if(!b2reg.test(a[i])) c += 2;
        else c++;
      }
    }

一般要求不严格的情况,我都是这么处理
但是,这个也不靠谱,中文没问题,但是英文字符的显示宽度,会受字体、浏览器的影响,比如“i”和“w”,很难确定这俩显示宽度是否相同

终结:实际显示宽度
有些复杂,不过思路也很简单:在页面的视口之外,做一个同样的显示区域,字体、样式什么的都一样,然后把文本写进去,再判断宽度;如果过宽,再截
这种方法还有一个经典的应用:输入框跟踪提示。
比如,在一个很大的输入框里,输入多个邮箱地址,要随着用户的输入,在输入点下方位置显示不同邮箱地址的提示,这里的一个难点就是:js里无法获得当前输入点的位置。于是就可以用上面的方法,时时获取显示宽度,然后用复杂的计算近似得到当前输入点的位置…

———
(好久不写技术文章了,一方面是懒,另外很关键的一点是…近半年都在做售货机屏幕的东西,缺少普适性)

Hello World

js调试之~打印调用堆栈

(很久没写东西,这里写一个自己熟悉的内容做2016的开篇吧)
相对于其他语言,js似乎很少有需要打印调用堆栈的时候,反正我是做了这么多年了前端,第一次用到,其他情况,基本都可以通过更好的方式来调试
今天面对的问题是这样的,有一个“ad_list”事件,在不停的触发,触发它的地方有N个,触发条件也不一样,靠简单的“console.log”,太多,可读性很差,所以想到了调用堆栈
不过,由于js里大部分是匿名函数,打印出来的堆栈并不像java、php一样简介。。。,打印名字不靠谱,而是,直接打印function内容,然后靠内容反查。。。。这就是很少有人用这一招的原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  if(action == 'ad_list'){//打印特定条件的堆栈
    var caller = arguments.callee.caller, j = 0, s;
    while (caller) {
      j++;
      if(typeof caller == 'function' || typeof caller == 'object'){
        s = (caller + '');//function变成字符串
        //排除系统函数、框架函数
        if(s.indexOf('function (event)') < 0 && s.indexOf('function (packet)') < 0 && s.indexOf('fn.apply') < 0){
          console.log('【function】-----stack-----------', j, ': ', (caller + '').substr(0, 200));//打印funciton的前半截
        }
      }
      caller = caller && caller.caller;//取下一级
      if(j > 500){//限定数量  ---  这个一定不要漏了
        return;
      }
    }
  }

————
查了半天(真是半天)的问题,结果是自己给自己挖的坑:默认的图片/活动名称是按规约命名的,但是测试图省事没照这个规则做,才造成上面说的不停的刷新同一个东西。。。。。。