# 2021-08 问题记录

2021/8/06 周五

# 1.button 点击不到

有一个 input 输入框,blur 的时候,会校验,不通过怎展示错误信息。错误信息会影响 button 的位置。因此在 input 有焦点的时候,点击 button,第一次点击不到...


2021/8/27 周五

# 2. vue 不触发 beforeDestroy

key 值更新,导致的组件重新渲染,不会触发 beforeDestroy


2021/8/30 周一

# 3. call.call

function a(arg){ 
    console.log(this, 'a', arg)
}
function b(arg){
    console.log(this, 'b', arg)
}
a.call(b,'b'); // fnb "a" "b"
a.call.call(b,'b'); // String {"b"} "b"  undefined
a.call.call.call(b,'b'); // String {"b"} "b"  undefined
1
2
3
4
5
6
7
8
9

ECMAScript5.1语言规范 (opens new window)

Function.prototype.call (thisArg , ...args) When the call method is called on an object func with argument, thisArg and zero or more args, the following steps are taken: 1.If IsCallable(func) is false, throw a TypeError exception. 2.Let argList be an empty List. 3.If this method was called with more than one argument then in left to right order, starting with the second argument, append each argument as the last element of argList. 4.Perform PrepareForTailCall(). 5.Return Call(func, thisArg, argList).

伪代码:

Function.prototype.call = function(thisArg, arg1, arg2, ...) {
  /*** 注意:this指向调用call的那个对象或函数 ***/
  // 1. 调用内部的IsCallable(this)检查是否可调用,返回false则抛TypeError
  if (![[IsCallable]](this)) throw new TypeError();
  
  // 2. 创建一个空列表
  // 3. 将arg1及后面的入参保存到argList中
  var argList = [].slice.call(arguments, 1);

  // 4. 调用内部的[[Call]]函数
  return [[Call]](this, thisArg, argList)
}
1
2
3
4
5
6
7
8
9
10
11
12

a.call(b, 'b') 可以理解为:

a.call = function(thisArg, arg1) {
  if (![[IsCallable]](a)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = b, arg1 = 'b'
  return [[Call]](a, thisArg, argList)
}
1
2
3
4
5
6

a.call.call(b,'b') 可以理解为:

// 先查看引用关系
a.call === Function.protype.call;  // true
a.call === a.call.call;  // true
a.call === a.call.call.call;  // true

// 因为前面 a.call 只是获取对象的 call 属性,最后一个 call 才执行了,所以倒着看
a.call.call = function(thisArg, arg1) {
  if (![[IsCallable]](a.call)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = b, arg1 = 'b'
  return [[Call]](a.call, thisArg, argList)  
};

a.call = function(thisArg, arg1, arg2) {
  if (![[IsCallable]](b)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = 'b', arg1 = undefined
  return [[Call]](b, thisArg, argList)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 举一反三:

Array.prototype.resolve = function(){
  this.forEach(Function.prototype.call, Function.prototype.call)
}
var cbs = [function(arg){console.log(1,this,arg)}, function(arg){console.log(2,this,arg)}]
cbs.resolve() 
// 控制台输出
// 1 , Number {0} , (2) [ƒ, ƒ]
// 2 , Number {1} , (2) [ƒ, ƒ]
1
2
3
4
5
6
7
8

先看 Array.prototype.forEach(fn, thisArg) 的伪代码: ECMAScript5.1语言规范 (opens new window)

Array.prototype.forEach = function(fn, thisArg){
  var item;
  for (var i = 0, len = this.length; i < len; ++i){
    item = this[i];
    fn.call(thisArg, item, i, this)
  }
}
1
2
3
4
5
6
7

所以最后执行的应该是 Function.prototype.call.call(Function.prototype.call,item,i,this) 伪代码:

Function.prototype.call.call = function(thisArg,arg1,arg2,arg3) {
  if (![[IsCallable]](Function.prototype.call)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = Function.prototype.call, arg1 = item, arg2 = i, arg3 = this
  return [[Call]](Function.prototype.call, thisArg, argList)
};

Function.prototype.call = function(thisArg,arg1,arg2) {
  if (![[IsCallable]](Function.prototype.call)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = item, arg1 = i, arg2 = this
  return [[Call]](Function.prototype.call, thisArg, argList)
};

Function.prototype.call = function(thisArg,arg1) {
  if (![[IsCallable]](item)) throw new TypeError();
  var argList = [].slice.call(arguments, 1);
  // thisArg = i, arg1 = this
  return [[Call]](item, thisArg, argList)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 总结:

  • 一个 call 第一个参数是 this,后面是 arg;
  • 两+个 call 第一个参数是最终执行的 fn ,第二个是 this,后面是 arg

参考 (opens new window) call, call.call, call.call.call, 你也许还不懂这疯狂的call (opens new window) 如何理解fn1.call.call(fn2)的结果 (opens new window) JS中的call与call.call (opens new window)

上次更新: 8/31/2021, 2:53:22 PM