call

特性

原生

function test(a, b) {
	console.log(this, a, b);
}

function test2() {}
test.call(); // window undefined undefined (严格模式下 this 为 undefined)
test.call(undefined, 1, 2); // window 1 2 (严格模式下传入undefined,test中this是undefined)
test.call({
	name: 'Lance',
  age: 27
}, 1, 2); // obj 1 2
test.call(test2, 1, 2); // test2 1 2
test.call(1, 1, 2); // 1 1 2
test.call(true, 1, 2); // true 1 2

Untitled

实现

const utilsModule = (function(Function) {
	Function.prototype.myCall = function(ctx) {
    // 传了 context 上下文就设置上,没传默认 window
    // 如果传了,就用 Object 包一下(因为有可能传基本类型的值)
  	ctx = ctx ? Object(ctx) : window;
    // 函数谁调用,this指向谁,那我们就把当前this(函数本身)挂载到 ctx 上
    ctx.originFn = this;
    // 从第二个参数开始,都是函数本身要接收的参数了,收集起来
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
    	args.push('arguments[' + i + ']');
    }
    // 使用 eval 执行函数
    var ret = eval('ctx.originFn(' + args + ')');
    // 删除临时方法 originFn
    delete ctx.originFn;
    return ret;
  }
})(Function);

export default utilsModule;

使用:

<!DOCTYPE html>
<html lang="en">
<body>
  <script type="module">
  import utilsModule from './index.js'
  function test(a, b) {
    console.log(this, a, b);
  }
  function test2() {}
  test.myCall(undefined, 1, 2); // window 1 2
  test.myCall({
  	name: 'Lance',
    age: 27
  }, 1, 2); // obj 1 2
  test.myCall(test2, 1, 2); // test2 1 2
  test.myCall(1, 1, 2); // 1 1 2
  test.myCall(true, 1, 2); // true 1 2
  </script>
</body>
</html>

Untitled

apply

特性

原生

function test(a, b) {
  console.log(this, a, b);
}
function test2() {}

test.apply(undefined, [1, 2]); // undefined 1 2
test.apply({
  name: 'Lance',
  age: 27
}, [1, 2]); // obj 1 2
test.apply(test2, [1, 2]); // test2 1 2
test.apply(1, [1, 2]); // 1 1 2
test.apply(1, null); // 1 undefined undefined
test.apply(1, undefined); // 1 undefined undefined
test.apply(1, function() {}); // 1 undefined undefined
test.apply(1, {}); // 1 undefined undefined
test.apply(1, 1); // Uncaught TypeError: CreateListFromArrayLike called on non-object

Untitled