1. 为什么要有new操作符?
JS 的 new 到底是干什么的? 本文指出,new 操作符其实就是个语法糖,用了new 可以减少做四件事:
- 不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
- 不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
- 不用 return 临时对象,因为 new 会帮你做;
- 不要给原型想名字了,因为 new 指定名字为 prototype。
2. 关于new的面试题
请问以下表达式输出什么
function Person(name) {
this.name = name
return 1;
}
var p1 = new Person('Tom')
function Person2(name) {
this.name = name
return {a:1};
}
var p1 = new Person2('Tom')
答案:第一个输出{name: 'Tom'},第二个输出{a:1}
评论区给了为什么得到的是这个答案,构造函数实例化的规则是,函数return引用类型的话,则new出来的实例对象就是该return引用类型,否则就是构造函数默认的实例对象
关于JavaScript new 的一些疑问?给了更加详细的答案,文中提到的创建数组实例加不加new关键字来自于《JavaScript高级程序设计》的86页,加不加new关键字都是一样的。
但是并不是所有的构造函数都可以不加new去实例化对象,对于Array构造函数来讲,不写new只是个省略写法。直接调用也和使用new调用一样,只是少写个单词而已。但要注意的是,这种表现不适用于自定义的构造函数。并且,即使是JS原生的构造函数,是否通过new关键字调用,可能具有相同的行为,比如Object, Array, RegExp,两种方式都是创建实例;也可能具有不同的行为,比如Date()、Boolean()、Number()、String(),它们使用new关键字调用函数创建的是实例对象(也叫做基本包装类型),而直接调用的结果是强制类型转换(它们与自定义构造函数的表现比较接近)。
3. 模拟new操作符实现
JS 的 new 到底是干什么的? 可以看出new就是个语法糖,抛开这个语法糖,我们自己用原始的方式实现就是了。
看了两个大佬的写法后,过几天,自己手动实现下,实现主要就是绑定this跟原型
function Person(age, name) {
this.age = age;
this.name = name;
}
Person.prototype.say = function() {
console.info("i am ", this.name);
};
// const p = new Person("geek", 88);
// console.info(p);
// 绑定this 绑定原型
function mockNew(constructor) {
const args = Array.prototype.slice.call(arguments);
// 或者用shift
const params = args.slice(1);
let instance = {};
const ret = constructor.apply(instance, params);
if (ret !== null && typeof ret === "object") {
return ret; // 如果返回的是对象,返回的对象的原型并没有继承自constructor.prototype
}
instance.__proto__ = constructor.prototype;
return instance;
}
const pp = mockNew(Person, 30, "geek");
console.info(pp);
pp.say();
不传constructor,使用shift拿到constructor
function mockNew() {
const args = Array.prototype.slice.call(arguments);
const constructor = args.shift();
let instance = {};
const ret = constructor.apply(instance, args);
if (ret !== null && typeof ret === "object") {
return ret; // 如果返回的是对象,返回的对象的原型并没有继承自constructor.prototype
}
instance.__proto__ = constructor.prototype;
return instance;
}
再或者用es6展开运算符
function mockNew(constructor, ...args) {
let instance = {};
const ret = constructor.apply(instance, args);
if (ret !== null && typeof ret === "object") {
return ret; // 如果返回的是对象,返回的对象的原型并没有继承自constructor.prototype
}
instance.__proto__ = constructor.prototype;
return instance;
}
因为__proto__
不是标准,所以可以使用Object.create绑定原型
function mockNew(constructor, ...args) {
let instance = Object.create(constructor.prototype);
// 这个时候constructor.prototype===instance.__proto__ 成立
const ret = constructor.apply(instance, args);
if (ret !== null && typeof ret === "object") {
return ret; // 如果返回的是对象,返回的对象的原型并没有继承自constructor.prototype
}
return instance;
}
再或者使用es6的Object.setPrototypeOf绑定原型
function mockNew(constructor, ...args) {
let instance = {};
// 这个时候constructor.prototype===instance.__proto__ 成立
Object.setPrototypeOf(instance, constructor.prototype);
const ret = constructor.apply(instance, args);
if (ret !== null && typeof ret === "object") {
return ret; // 如果返回的是对象,返回的对象的原型并没有继承自constructor.prototype
}
return instance;
}
网友评论