call/apply源码解析
call和apply的功能相同,都是改变 this 的执行,并立即执行函数。两者只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
this
this指向
- 作为对象的方法调用 - 指向对象(
apply/call实现的原理部分) - 作为普通函数调用 - 指向全局上下文
- 构造器调用(new) - this指向函数的返回对象。若构造函数显示返回了Object类型对象,this则指向这个对象
- call/apply调用,指定对应的对象
call(thisArg, arg1, arg2, ...)
apply()和call()方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)/参数列表的形式提供的参数。两者实现的功能:
- 修改this的指向
- 传入参数,并执行函数
- 返回值:调用有指定this值和参数的函数的结果。
Function.prototype.call = function () {
// 获取thisArg和对应的参数
let [thisArg, ...args] = [...arguments];
// 判断传入的thisArg是否为null或undefined,默认全局
if (!thisArg) {
thisArg = typeof window === 'undefined' ? global : window;
}
// 将当前方法fn挂到thisArg上
// this的指向: 作为对象的方法调用,this则会指向该对象 则fn中的this会指向thiArg
// 如果fn上有自带的属性: fn上的属性添加到thisArg上,因thisArg是引用类型,同时影响外部变量thisArg
thisArg.func = this;
// 指向函数并传入参数
let result = thisArg.func(...args);
// 删除当前对象中的func
delete thisArg.func;
return result;
}
apply(thisArg, [argsArray])
Function.prototype.apply = function (thisArg, argsArray = []) {
// 判断传入的thisArg是否为null或undefined,默认全局
if (!thisArg) {
thisArg = typeof window === 'undefined' ? global : window;
}
// 将当前方法fn挂到thisArg上
// this的指向: 作为对象的方法调用,this则会指向该对象 则fn中的this会指向thiArg
thisArg.func = this;
// 指向函数并传入参数
let result = thisArg.func(...argsArray);
// 删除当前对象中的func
delete thisArg.func;
return result;
}
apply(thisArg, [argsArray])和call(thisArg, arg1, arg2, ...)会对thisArg产生副作用,若fn中有自带的属性,则会默认添加到thisArg上。call/apply副作用产生的原理:值复制
let thisArg = {
name: 'apply的调用方法',
age: 18,
sex: 'female'
};
function getName () {
this.userName = '焦糖瓜子';
this.job = '前端搬砖工';
}
let result = getName.apply(thisArg);
console.log('result', result); // undefined
console.log('thisArg', thisArg);
new关键字
new关键字创建对象的过程
- 在内存中创建一个新对象
- 将新对象内部的
__proto__([[prototype]])被赋值为构造删除的prototype属性 - 构造函数内部的this被赋值为这个新对象(即this指向新对象)-
- 执行构造函数内部的代码(给新对象添加属性:
将构造函数内部属性方法赋给新对象) - 如果构造函数返回非空对象,则返回该对象;否则返回刚创建的对象
注意:若构造函数中返回this或返回值是基本类型(number、string、boolean、null、undefined)的值,则返回新实例对象;若返回值是引用类型的值,则实际返回值为这个引用类型
function _new() {
// 获取构造函数与参数
let [constructor, ...args] = [...arguments];
// 创建新对象
let obj = {};
// 原型关联
obj.__proto__ = constructor.prototype;
// 将构造函数内部this指向obj/执行构造函数 - apply副作用会将构造函数的属性与方法添加给 obj
let result = constructor.apply(obj, args);
// 判断构造函数返回值,如果对象则返回该对象,否则 返回刚创建的对象
if (result && (typeof result === 'object' || typeof result === 'function')) {
return result;
}
return obj;
}
function PersonProto() {
this.name = 'Zaxlct';
this.age = 18;
this.job = 'Software Engineer';
}
PersonProto.prototype.sayName = function() {
alert(this.name);
}
let person = _new(PersonProto);
console.log('person', person);
let person1 = new PersonProto();
console.log('person1', person1);
优先级
优先级由高到低:小括号(xxx) ---> 属性访问. ---> new foo() ----> foo()
function getName(){
console.log(1)
}
function Foo() {
this.getName = function () {
console.log(2);
};
return this;
}
Foo.getName = function () {
console.log(3);
};
//先从.属性访问符号开始往前面找一个最近的对象,同时注意new Foo()优先于Foo();
var a=new Foo.getName(); //3;===new (Foo.getName)();返回Foo.getName类型的实例
var b=new Foo().getName() ; //2;===(new Foo()).getName();返回undefined
var c=new new Foo().getName(); //2;===new (new Foo().getName)();返回Foo.getName类型的实例
new Date().getTime(); //===((new Date()).getTime)()
(new Date).getTime();
new Date.getTime(); //Uncaught TypeError: Date(...).getTime is not a function;===new (Date.getTime)()