new操作符
new操作符会进行四步:
- 创建一个空对象
- 把空对象和构造函数的prototype关联起来
- 把构造函数的this绑定到空对象上
- 根据构造函数的返回值类型返回结果,如果是基础类型就返回空对象,如果是引用类型就返回引用类型
JavaScript
function newFn(Fun, ...args) {
// 1. 创建一个空对象
let obj = {};
// 2. 把空对象和构造函数的prototype关联起来
obj.__proto__ = Fun.prototype;
// 3. 把构造函数的this绑定到空对象上,当构造函数有返回值时,result为返回值,否则为undefined
const result = Fun.apply(obj,args)
// 4. 根据构造函数的返回值类型返回结果,如果是基础类型就返回空对象,如果是引用类型就返回引用类型
return result instanceof Object ? result : obj;
}
function person(name) {
this.name = name;
}
person.prototype.say = () => {
console.log('说话');
};
let obj = newFn(person, "Rarrot");
obj.say(); // 说话
console.log(obj) // person {name: 'Rarrot'}
为什么要这么写呢?
js
function Person() {
this.name = 'Rarrot';
// 检验第三步时打开下面这行代码
// return { name: 'new Rarrot' };
}
let person = new Person();
// 检验第一步,原型的指向
person.__proto__ === Person.prototype; // true
// 检验第二步,this的指向
console.log(person); // Person {name: 'Rarrot'}
// 检验第三步,为什么要返回引用类型
// 当Person中返回基础类型,不会变化;当返回引用类型,会返回引用类型
console.log(person); // {name: 'new Rarrot'}
注意
引用类型之间通过new操作符创建的对象是不相等的,例如:
JavaScriptfunction Person() { this.name = 'Rarrot'; } let person1 = new Person(); let person2 = new Person(); console.log(person1 === person2); // false console.log([ 1 ] === [ 1 ]); // false
对象的key都是字符串,如果是引用类型也会自动转换为字符串,例如:
JavaScriptlet obj = {}; let arr1 = { a: 1 }; let arr2 = { b: 2 }; obj[arr1] = 1; obj[arr2] = 2;// arr1.toString() === arr2.toString(),所以会覆盖掉arr1 for (let key in obj) { console.log(key + ':' + obj[key]); // [object Object]:2 }
对象找属性|方法的顺序:
在对象本身找 ==> 在构造函数上找 ==> 在对象原型上找 ==> 构造函数原型上找 ==> 对象上一级找 ==> Object.prototype找
JavaScriptfunction Person() { this.name = 'Rarrot'; this.age = 777; // 在构造函数中添加 } Person.prototype.age = '999'; // 在构造函数原型上添加 let person = new Person(); person.age = '666'; // 在对象本身添加 person.__proto__.age = '888'; // 在对象原型上添加 Object.prototype.age = '000'; // 在Object上添加 console.log(person.age); // 666 console.log(person.__proto__ === Person.prototype); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null
object
为什么在Object 参考页,会发现左侧列出许多属性和方法——大大超过我们在自己定义的对象中看到的继承成员的数量。
原因在于,继承的属性和方法是定义在 prototype 属性之上的(你可以称之为子命名空间 (sub namespace) )——那些以 Object.prototype. 开头的属性,而非仅仅以 Object. 开头的属性。
prototype 属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中,而不希望被继承的则放于Object自身,记住每一个对象都有一个原型(prototype)属性,其指向另一个对象。由下图可以看出,prototype内部存在有许多的属性和方法,原因即为以上所述
于是 Object.prototype.watch()、Object.prototype.valueOf() 等等成员,适用于任何继承自 Object() 的对象类型,包括使用构造器创建的新的对象实例。
Object.is()、Object.keys(),以及其他不在 prototype 对象内的成员,不会被“对象实例”或“继承自 Object() 的对象类型”所继承。这些方法/属性仅能被 Object() 构造器自身使用。