原型是什么
- 所有
引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
const arr = []
console.log(typeof arr.__proto__); // object
- 所有
引用类型的__proto__属性指向它构造函数的prototype
console.log(arr.__proto__ === Array.prototype); // true
- 当一个对象在查找一个属性的时候,自身没有就会根据__proto__向它的原型进行查找,终点是
Object.prototype.__proto__为null,查找的过程形成了原型链
console.log(arr.toString); // [Function: toString]
探索原型链
小伙伴们可以移步到JavaScript 世界万物诞生记,一篇帮助理解原型链的好文👀
下面请出万恶之源
- 为什么需要原型和原型链
function Person(name) {
this.name = name;
this.sayname = function() {
console.log(name);
}
}
let p1 = new Person('xguo');
let p2 = new Person('xguo');
console.log(p1.sayname === p2.sayname); // false
用构造函数调用生成实例对象,有一个缺点,无法共享属性和方法。每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费,考虑到这些,原型和原型链诞生了
function Person(name) {
this.name = name;
}
Person.prototype.sayname = function() {
console.log('----');
}
let p1 = new Person('xguo');
let p2 = new Person('xguo');
console.log(p1.sayname === p2.sayname); // true
- 如何理解原型链
我们知道Object.prototype.__proto__ === null,为什么呢?因为JS从设计之初就想用null表示空对象,最直接的证据:typeof null === 'object';因此ES5中用Object.create(null)来生成没有原型的空对象
console.log(Object.prototype.__proto__ == Object.create(null).__proto__); // true
也就是说,Object.prototype 是使用这种方式生成的,然后才绑在 Object 的 prototype 属性上,因为对象可以先生成再赋予新属性
console.log(Function.__proto__=== Function.prototype); // true
Function 是一个函数,只要是函数,它的__proto__就指向 Funtion.prototype这是继承的设定。Function 生成时,是没有__proto__属性的,它是一个BOM对象,Function 的__proto__和prototype属性都是后面才指向同一个 BOM 对象的
console.log(Object instanceof Function) // true
console.log(Object.__proto__=== Function.prototype); // true
Object是构造器函数, 因此它的__proto__属性会指向Funtion.prototype
function Person() {}
console.log(typeof Person.prototype);
console.log(typeof Function.prototype);
Function.prototype是一个函数,也是对象,只有Function的prototype是函数其他都是普通对象。它是一个桥梁,我们一直说函数也是对象,只有这个满足了, 函数才能是对象
“构造函数” new
function Cat(name, color) {
// 像其他语言的封装性 降低其他语言开发者的门槛
this.name = name
this.color = color
}
var cat1 = new Cat('狗蛋', '白色')
console.log(cat1.constructor === Cat); // true
我们使用new关键字,看起来是执行了类的构造函数方法,Cat的首字母也大写了,并且cat1.constructor === Cat,一切都好像在提示它是一个类
-
构造函数还是调用 实际上
Cat和程序中的其他函数没有任何区别,幕后黑手是new,它会劫持所有普通函数并用构造对象的形式调用它。换句话说,在JavaScript中对于“构造函数”最准确的解释是,所有带new的函数调用 -
.constructor属性
console.log(cat1.constructor === Cat.prototype.constructor); // true
实际上,Cat.prototype.constructor默认指向Cat,这和“构造”毫无关系。
function Cat() {}
Cat.prototype = {}
var cat1 = new Cat()
console.log(cat1.constructor === Cat); // false
console.log(cat1.constructor === Object); // true
.constructor属性只是Cat函数在声明时的默认属性,如果创建一个新对象并替换函数默认的.prototype引用,那么新对象并不会自动获得.constructor属性
- 手动实现简版new
function Person(name, age, sex="女") {
// console.log(arguments)
this.name = name;
this.age = age;
this.sex = sex;
}
// 实例除了属性各有各的(私有的 constructor)通过prototype
// 对象间共享的方法(prototype )
// construtor 完成对象构造,再使用prototype连接共享方法
Person.prototype.sayHi = function() {
console.log('你好啊');
}
// 手动实现new
const objectFactory = function() {
var obj = new Object();
// [].shift [] 对象 shift 上的方法
// this -> [] call -> arguments
// console.log(Array.from(arguments).shift());
var varConstructor = [].shift.call(arguments);
varConstructor.call(obj, ...arguments);
obj.__proto__ = varConstructor.prototype;
return obj;
}
const obj = objectFactory(Person, 'xguo', 19, '男');
console.log(obj); // Person {name: 'xguo', age: 19, sex: '男'}
obj.sayHi(); // 你好啊