探究JavaScript中new对象的过程,call()、apply()方法的实现原理
function Person(name, age) {
this.name = name;
this.age = age;
console.log("Person...");
}
// let p = new Person("张三", 18);
// console.log(p);
/**
* 【目标1:模拟js中new运算符的功能。传入一个构造函数和可选的构造参数,返回对应的构造实例】
* js new运算符内部原理:1.创建一个空对象;2.修改构造函数内部的this指向为空对象;3.执行构造函数的代码;4.返回创建的对象
* (当构造函数内部显式返回基本类型或null时等价于不返回,new操作默认会返回this)
*/
function MyNew(constructor, ...args) {
if (!constructor || !constructor.hasOwnProperty("prototype")) {
throw new TypeError("constructor is not a Constructor");
}
let obj = Object.create(constructor.prototype); // 创建空对象{}并绑定对象的原型为参数对象constructor.prototype
let res = constructor.apply(obj, args);
if (res != null && (typeof res == "object" || typeof res == "function")) { // typeof null = 'object'
return res;
}
return obj;
}
// console.log(MyNew("string类型", '字符串类型', 3)); // Uncaught TypeError: constructor is not a Constructor
console.log(MyNew(Person, "李四", 21)); // Person... Person {name: '李四', age: 21}
console.log(MyNew(Person, "李四")); // Person... Person {name: '李四', age: undefined}
console.log(MyNew(function(){console.log('xxx')})); // xxx {}
/**********************************************************************/
/**
* 【目标2:模拟js中Object类的call方法】
* call方法原理:1.修改调用者函数内部的this指向为call的第一个参数,默认为window;2.执行函数并返回结果。(参数为可变参数)
*/
Function.prototype.myCall = function (context) {
context = context ? Object(context) : window; // 如果调用时没有传参数,则指定执行上下文为window,否则按需转换为Object引用类型
context._tempFunc = this; // 临时绑定调用者函数
let args = []; // 收集调用者函数的参数,arguments为伪数组
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
// let res = this(...args); // (错误写法)独立函数调用,内部this指向window,而非context(不合要求,因为call函数显式绑定this)
let res = context._tempFunc(...args); // 隐式绑定调用者函数内部的this为context
delete context._tempFunc; // 释放临时函数
return res;
};
function testFunc(n1, n2) {
console.log(this);
return n1 + n2;
}
console.log(testFunc.myCall()); // window NaN
console.log(testFunc.myCall("hello", 10, 20)); // String {'hello'} 30
console.log(testFunc.myCall({ a: 'value a' }, 20, 30)); // {a: 'value a'} 50
/**********************************************************************/
/**
* 【目标3:模拟js中Object类的apply方法】
* apply方法原理:1.修改调用者函数内部的this指向为apply的第一个参数,默认为window;2.执行函数并返回结果。(参数为数组)
*/
Function.prototype.myApply = function (context, args) {
context = context ? Object(context) : window; // 如果调用时没有传参数,则指定执行上下文为window,否则按需转换为Object引用类型
context._tempFunc = this; // 临时绑定调用者函数
// let res = this(args); // (错误写法)独立函数调用,内部this指向window,而非context(不合要求,因为apply函数显式绑定this)
let res = context._tempFunc(args); // 隐式绑定调用者函数内部的this为context
delete context._tempFunc; // 释放临时函数
return res;
};
function testFunc(n1, n2) {
console.log(this);
return n1 + n2;
}
console.log(testFunc.myApply()); // window NaN
console.log(testFunc.myApply("hello", [10, 20])); // String {'hello'} 30
console.log(testFunc.myApply({ a: 'value a' }, [20, 30])); // {a: 'value a'} 50