this执行主体,谁把它执行的「和在哪创建&在哪执行都没有必然的关系」
call
和apply
的唯一区别是参数,第一个参数都是要绑定的上下文
call
可以有多个参数,剩余参数被认为是传递给方法的参数
apply
第二个参数为一个数据,是传递给方法参数的集合
func.call(obj, 10, 20);
func.apply(obj, [10, 20]);
原理:利用**'点’定THIS**机制,context.xxx=self “obj.xxx=func” => obj.xxx()
注意:原始值比如n=10;n.xxx=100
不报错,但是n.xxx
拿不到,所以判断如果传入的this上下文不是对象,转为对象
Function.prototype.call = function call(context, ...params) {
// this/self->func context->obj params->[10,20]
let self = this,
key = Symbol('KEY'), //加的属性不能影响原始obj中的属性
result;
context == null ? context = window : null;
!/^(object|function)$/i.test(typeof context) ? context = Object(context) : null; //原始值加属性不报错,但是拿不到值 判断是否为对象 不是则改为对象
context[key] = self;
result = context[key](...params);
delete context[key];
return result;
};
Function.prototype.bind = function bind(context, ...params) {
// this/self->func context->obj params->[10,20]
let self = this;
return function proxy(...args) {
// 把func执行并且改变this即可 args->是执行proxy的时候可能传递的值
self.apply(context, params.concat(args));
};
};
普通函数的调用 + 柯里化的实现
如果是正常的普通函数的调用方法来调用bind返回的函数,那么直接返回像调用call函数那样返回,第一个参数值就是传入bind的asThis值。注意要将前后传入的参数(两次…arg收集到的)同时收集到call函数的参数中,从而实现柯里化。
通过new操作符调用 + 柯里化的实现
如果通过new来调用bind生成的函数(绑定函数)情况又有所不同了
Function.prototype.bind2 = function(asThis,...args) {
//这里的this就是调用bind2的函数(普通函数this指向调用者)
if (typeof this !== "function") {
throw new TypeError("Not a Function");
}
// 这里保存调用bind的函数以及传给bind后面的参数。
var origin = this;
let args0 = args;
function bound(...args) {
if(this instanceof bound)
//如果是new的形式来使用绑定函数的
return new origin( ...args0,...args)
else
//如果是普通函数的形式来使用绑定函数的(基本上与call、apply无区别,实现柯里化即可)
return origin.call(asThis, ...args0,...args);
}
bound.prototype = origin.prototype;
return bound;
}
类数组像数组「结构、一些操作…」,我们让其用数组上的方法「不能直接用」
/* Array.prototype.slice = function slice() { 不传参数是浅拷贝,克隆arr返回一个新数组
let result = [];
for (let i = 0; i < this.length; i++) {
result.push(this[i]);
}
return result;
}; */
// 克隆arr返回一个新数组
// arr.slice();
// 掌握this的好玩应用:鸭子类型
// 像鸭子,我们就说他是鸭子 -> 类数组像数组「结构、一些操作...」,我们让其用数组上的方法「不能直接用」
function func() {
console.log(arguments); //js把传入到这个函数的全部参数存储在一个叫做arguments的东西里面
// 把arguments变为数组,这样就可以用数组的办法了:Array.from/[...arguments]/...
let result = [];
for (let i = 0; i < arguments.length; i++) {
result.push(arguments[i]);
}
// Array.prototype.slice -> [].slice
// let result = Array.prototype.slice.call(arguments);
// console.log(result);
[].forEach.call(arguments, item => {
console.log(item);
});
}
func(10, 20, 30);
new绑定》显示绑定》隐式绑定》默认绑定
默认绑定:独立函数调用,可以把默认绑定看作是无法应用其他规则时的默认规则,this指向全局对象。
var name = 'Jenny';
function person() {
return this.name;
}
console.log(person()); //Jenny
隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。
函数还可以作为某个对象的方法调用,这时this就指这个上级对象
function test() {
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m(); // 1
这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象