创建对象和生成原型链 的正确打开方式
使用语法结构创建的对象
var o = {a: 1};
// o 这个对象继承了 Object.prototype 上面的所有属性;
//因此 o 继承了 Object.prototype 的 hasOwnProperty
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于 Function.prototype
// (Function.prototype 中包含 call, bind 等方法)
// 原型链如下:
// f ---> Function.prototype ---> Object.prototype ---> null
使用构造器创建的对象
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 [new 操作符] 来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
addVertex: function(v){
this.vertices.push(v);
}
};
var g = new Graph();
// g 是生成的对象,他的自身属性有 'vertices' 和 'edges'。
使用 Object.create 创建的对象
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 (继承而来)
使用 class 关键字创建的对象
ECMAScript6 引入了一套新的关键字用来实现 [class]。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript 仍然基于原型。
```javascript
class Animal {
constructor(name) {
this.name = name;
}
sayName() {
console.log('My name is ' + this.name);
}
}
class Cat extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
sayColor() {
console.log('My color is ' + this.color);
}
}
const garfield = new Cat('Garfield', 'orange');
在上述代码中,使用 class 关键字定义了 Animal 和 Cat 两个类,分别用来创建 Animal 和 Cat 对象。通过使用 extends 关键字实现了 Cat 对象继承 Animal 对象的属性和方法。
性能
在遍历原型链上查找属性比较耗时,对性能有副作用。
但是hasOwnProperty
是 JavaScript 中唯一一个处理属性并且不会遍历原型链的方法。
注意:检查属性是否为 [
undefined
] 是不能够检查其是否存在的。该属性可能已存在,但其值恰好被设置成了undefined
。
错误实践:扩展原生对象的原型
经常使用的一个错误实践是扩展 Object.prototype
或其他内置原型。
这种技术被称为猴子补丁并且会破坏封装。尽管一些流行的框架(如 Prototype.js)在使用该技术,但仍然没有足够好的理由使用附加的非标准方法来混入内置原型。
扩展内置原型的唯一理由是支持 JavaScript 引擎的新特性,创建 polyfill,本质上为老版本浏览器缺失的方法提供自己的实现,该方法是由 JavaScript 规范定义的。