js常见问题

97 阅读5分钟

1.毫秒数问题

        console.log(new Date("2017-1-8").getTime()) //错误写法,不是所有浏览器兼容得到毫秒数谷歌:1483804800000火狐;1483833600000safari:NaN

 console.log(new Date("2017-01-08").getTime())//错误写法,不是所有浏览器兼容得到毫秒数谷歌1483833600000火狐1483833600000safari:1483833600000

       console.log(new Date("2017/01/08").getTime())//正确写法得到毫秒数1483804800000

       console.log(new Date("2017/1/8").getTime())//正确写法得到毫秒数1483804800000

 

2. call apply bind 的作用以及区别

在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。(借用他人的方法,借用他人的上下文)。

bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;

apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;

apply 、 call 、bind 三者都可以利用后续参数传参;

bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

 

3.js链式操作

链式调用我们平常用到很多,比如jQuery中的(ele).show().find(child).hide(),再比如angularjs中的(ele).show().find(child).hide(),再比如angularjs中的http.get(url).success(fn_s).error(fn_e)。但这都是已经包装好的链式调用,我们只能体会链式调用带来的方便,却不知道形成这样一条函数链的原理是什么。

  随着链式调用的普及,实现的方案也越来越多。最常见的,是jQuery直接返回this的方式,underscore的可选式的方式,和lodash惰性求值的方式。我们分别来了解,并逐个完成它们的demo。

 (1)直接返回this是最常见的方式,也是所有方式的基础

function A(num) {   this.value = num || 0;  //不做传参校验了 }

A.prototype.add = function(a) {this.value += a; return this;} A.prototype.reduce = function(a) {this.value -= a; return this;}//prototype 属性使您有能力向对象添加属性和方法。

var a = new A(2); console.log(a.add(1).reduce(2))

 

   (2)underscore中用到chain。underscore规定了两种调用方式,.forEach(arr, fn);.map(arr, fn);和_.chain(arr).forEach(fn).map(fn)

var _ = function(array) {     this._value = Array.prototype.slice.apply(array);   }   _.forEach = function(array, fn) {     array.forEach(function(v, i, array) {       fn.apply(v, [v, i, array]);     })   };   _.map = function(array, fn) {     return array.map(function(v, i, array) {       return fn.apply(v, [v, i, array]);     })   };   _.chain = function(array) {     return new _(array);   }

for(var i in ) { //首先我们要遍历     if(i !== 'chain') { //然后要去除chain       .prototype[i] = (function(i) { //把其他的方法都经过处理赋给.prototype         return function() { //i是全局变量,我们要通过闭包转化为局部变量           var args = Array.prototype.slice.apply(arguments);  //取出新方法的参数,其实就fn一个           args.unshift(this._value);  //把_value放入参数数组的第一位           if(i === 'map') { //当方法是map的时候,需要修改_value的值             this._value = _[i].apply(this, args);           }else { //当方法是forEach的时候,不需要修改_value的值             _[i].apply(this, args);           }           return this;         }       })(i);     }   }

_.prototype.value = function() {     return this._value;   }

var a = [1, 2, 3];     _.forEach(a, function(v) {

    console.log(v);

})

console.log(_.map(a, function(v) {

     return ++v;

}))

console.log(_.chain(a).map(function(v) {

     return ++v;

}).forEach(function(v) {

     console.log(v);

}).value())

 

     (3)lodash惰性调用又是怎样的呢?首先我来解释下什么是惰性调用,比如上面的_.chain(arr).forEach(fn).map(fn).value(),当执行到chain(arr)的时候,返回了一个对象,执行到forEach的时候开始轮询,轮询完再返回这个对象,执行到map的时候再次开始轮询,轮询完又返回这个对象,最后执行到value,返回对象中_value的值。其中每一步都是独立的,依次进行的。而惰性调用就是,执行到forEach的时候不执行轮询的操作,而是把这个操作塞进队列,执行到map的时候,再把map的操作塞进队列。那什么时候执行呢?当某个特定的操作塞进队列的时候开始执行之前队列中所有的操作,比如当value被调用时,开始执行forEach、map和value。

function Task() {

   this.queen = [];

   this.queenIndex = 0;

   this.loopCount = 0;

   this.loopIndex = 0;

   this.loopStart = 0;

}

 

var _task_proto = {

   loop: function(num) {

     this.loopStart = this.queenIndex;

     this.loopCount = num;

   },

   job: function(str) {

     console.log(str);

   },

   end: function() {

     this.loopIndex++;

     if (this.loopIndex < this.loopCount) {

       this.queenIndex = this.loopStart;

     } else {

       this.loopIndex = 0;

     }

   },

   done: function() {

     console.log('done');

   }

};

Task.prototype.proto = _task_proto;

 

for (var i in _task_proto) {

   (function(i) {

     var raw = Task.prototype[i];

     Task.prototype[i] = function() {

       this.queen.push({

         name: i,

         fn: raw,

         args: arguments

       }); //保存具体的实现方法、名字和参数到任务队列

          

       return this;

     };

   })(i);

}

 

Task.prototype.next = function() {

   var task = this.queen[this.queenIndex]; //取出新的任务

   task.fn.apply(this, task.args); //执行任务中指向的具体的实现方法,并传入之前保存的参数

  if (task.name !== 'done') {

     this.queenIndex++;

     this.next(); //如果没执行完,任务索引+1并再次调用next

  } else {

     this.queen = [];

     this.queenIndex = 0; //如果执行完了,清空任务队列,重置任务索引

   }

}

 

for (var i in _task_proto) {

   (function(i) {

     var raw = Task.prototype[i];

     Task.prototype[i] = function() {

       this.queen.push({

         name: i,

         fn: raw,

         args: arguments

       }); //保存具体的实现方法、名字和参数到任务队列

       if (i === 'done') {

         this.next();

      }

       return this;

     };

   })(i);

}

 

var t = new Task();

console.log('1')

t.job('fuck').loop(3).job('world').end().loop(3).job('world').end().job('!').done();

console.log('2')

t.job('fuck').loop(3).job('world').job('!').end().done();

console.log('3')

t.job('fuck').loop(3).job('world').job('!').end().job('!');

 

4.jQuery的deferred对象

简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。(函数返回的是deferred对象,这就可以加上链式操作了)。

deferred.resolve()方法:执行状态变成"已完成"(resolved),deferred对象立刻调用done()方法指定的回调函数。

deferred.reject()方法:执行状态变成"已失败",调用fail()方法指定的回调函数。

deferred.promise()方法:它的作用是,在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。

www.ruanyifeng.com/blog/2011/0…

 

5.javascript 匿名函数的理解(当一个匿名函数被括起来,然后再在后面加一个括号,这个匿名函数就能立即运行起来)

(function(){ 

//这里忽略jQuery所有实现 

})(); 

(function(){ //这里忽略jQuery所有实现 })(); 

 

('.right').html());        })(jQuery)     }();

首先函数会构建作用域,把jQuery传进去,可以减少作用域查找。

如果不把jQuery当参数传递,则你使用变量jQuery/$的时候,首先会在函数体内查找,然后进fn查找,最后到window下查找。

而你把jQuery传进去之后,则只需要在函数体内查找$即可。

如果以后依赖的是Zepto/jqlite,你只需要改下参数就行了,在这种极端的情况下,降低了代码修改和可复用成本。

 

6.es5 和es6写法上下文对象不统一问题

(1)

_changeAdLink = (value, e) => {

    console.log(this)//this为react对象

}

(2)

_changeAdLink (value, e) {

    console.log(this)//this为元素对象

}

 

7.vue中计算者实现以及闭包问题

//vue中计算者实现(www.cnblogs.com/kidney/p/73…

var Dep = null;

 

function defineReactive(obj, key, val) {

var deps = [];

Object.defineProperty(obj, key, {

get: function() {

if (Dep) {

deps.push(Dep);

}

return val;

},

set: function(newVal) {

val = newVal;

deps.forEach(func => func());

}

});

};

 

function defineComputed(obj, key, func) {

func = func.bind(obj);

var value;

Dep = function() {

value = func();

}

value = func();

Dep = null;

Object.defineProperty(obj, key, {

get: function() {

return value;

}

});

};

 

var obj = {};

 

defineReactive(obj, 'a', 0);

defineComputed(obj, 'b', function() {

var a = this.a;

return a + 1;

});

 

 

//闭包问题

var a = 1;

var fun1, fun4;

var fun2 = function(fun3) {

var value;

fun1 = function() {

value = fun3();

}

value = fun3();

fun4 = function() {

return value;

}

}

 

fun2(function() {

return a + 1;

});

 

fun4()??

 

a = 2

 

fun4()??

 

fun1()

 

fun4()??