Skip to content

Latest commit

 

History

History
129 lines (97 loc) · 5.25 KB

原型链对象以及构造函数.md

File metadata and controls

129 lines (97 loc) · 5.25 KB

原型链、对象和构造函数之间的关系

一个例子

function Animal() {
}
let cat = new Animal()
console.log(cat.__proto__ === Animal.prototype) // true

上述例子中,cat 就是对象, Animal就是构造函数,通过构造函数创建的对象,他们之间必然有一道与之关联的桥梁(我们姑且这么描述),我们称之为原型链

原型链

每个对象都有一个私有属性(称之为 [[Prototype]]),它持有一个连接到另一个称为其 prototype 对象(原型对象)的链接。该 prototype 对象又具有一个自己的原型,层层向上直到一个对象的原型为 null。(译者注:Object.getPrototypeOf(Object.prototype) === null; // true)根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

JavaScript 中几乎所有的对象都是位于原型链顶端的Object的实例。

原型继承经常被视为 JavaScript 的一个弱点,但事实上,原型继承模型比经典的继承模型更加强大。例如,在一个原型模型之上构建一个经典模型是相当容易的。

遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject的原型。从 ECMAScript 6 开始,[[Prototype]] 可以用Object.getPrototypeOf()Object.setPrototypeOf()访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__

它不应该与函数(function)的func.prototype属性相混淆,func.prototype的作用是使用 new func()创建的对象的实例的 [[Prototype]]Object.prototype属性表示Object的原型对象。

每个函数都有一个 prototype 属性,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例原型,也就是下面的 cat 对象的原型

对象(实例)

这里所说的对象,也就是通过构造函数创建的实例,当读取实例的属性时,如果找不到,就会通过原型链查找与实例关联的原型中的属性,如果没有,继续向上查找原型的原型,直至顶层 null 为止,如果找不到则返回 undefined。

function People() {
	
}

let a = new People()
People.prototype.name = 'Gushaohua'
a.name = 'Douzi'

// 此时访问的是a的自身属性,这时候原型上的name访问不到,也称之为属性遮蔽(property shadowing)
console.log(a.name) // Douzi

// 删除a的自身属性,此时访问的则是原型People上的name属性
delete a.name
console.log(a.name) // Gushaohua

// a和原型People上都不存在sex属性,返回undefined
console.log(a.sex)  // undefined

对象上还有一个constructor属性,自身并没有这个属性,它指向其实就是构造器,也就是创建这个实例对象的构造函数,所以如下

a.constructor === People.prototype.constructor

构造函数

在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。

ECMAScript 5 中引入了一个新方法:Object.create()。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

ECMAScript 6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。
"use strict";

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}
var square = new Square(2);

性能

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。如早期遍历对象的for ... in方法

遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从Object.prototype继承的 hasOwnProperty 方法。下面给出一个具体的例子来说明它:

let parent = { a: 1, b: 2, c: 3 };
let child = { d: 4, e: 5, [Symbol()]: 6 };

//设置child对象中的d属性为不可枚举
Object.defineProperty(child, "d", { enumerable: false });

parent.__proto__ = child

for ( let k in parent ) {
	console.log(k) // a,b, c, e
}

for (var i in parent) { 
	if (parent.hasOwnProperty(i)) { 
		console.log(i);  // a, b, c
	} 
} 

Object.keys(parent).forEach( key => {
	console.log(key) // a, b, c
})