(一)需求
最近在看八股文,看到了原型链看到的知识点。开发时,可能用的1-2种多一些。没想到有这么多。MDN上,我发现有的点和控制台打印的有不一致的地方。就想着自己记录下来,之后有问题也便于查找和修整。
(二)四种方式
1、使用语法结构创建的对象
var o = {a: 1};
// o 这个对象继承了 Object.prototype 上面的所有属性
// o 自身没有名为 hasOwnProperty 的属性
// hasOwnProperty 是 Object.prototype 的属性
// 因此 o 继承了 Object.prototype 的 hasOwnProperty
// Object.prototype 的原型为 null
// 原型链如下:
// o ---> Object.prototype ---> nullvar a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// (Array.prototype 中包含 indexOf, forEach 等方法)
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> nullfunction f(){
return 2;
}// 函数都继承于 Function.prototype
// (Function.prototype 中包含 call, bind等方法)
// 原型链如下:
// f ---> Function.prototype ---> Object.prototype ---> null
2、使用构造器创建的对象
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.vertices = [];
this.edges = [];
}Graph.prototype = {
addVertex: function(v){
this.vertices.push(v);
}
};
var g = new Graph();
console.log(g)
// g 是生成的对象,他的自身属性有 'vertices' 和 'edges'。
// 在 g 被实例化时,g.[[Prototype]] 指向了 Graph.prototype。
3、使用
Object.create
创建的对象ECMAScript 5 中引入了一个新方法:
Object.create()
。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数:var a = {a: 1};
// a ---> Object.prototype ---> nullvar b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a);
// 1 (继承而来)var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> nullvar d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);
// undefined, 因为d没有继承Object.prototype
4、使用
class
关键字创建的对象ECMAScript6 引入了一套新的关键字用来实现 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);
(三)四种创建对象的优势和缺点对比
名称 | 例子 | 优势 | 缺陷 |
---|---|---|---|
New-initialization | function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto = new foo;
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Copy to Clipboard |
支持目前以及所有可想象到的浏览器(IE5.5都可以使用)。 这种方法非常快,非常符合标准,并且充分利用JIT优化。 | 为使用此方法,必须对相关函数初始化。 在初始化过程中,构造函数可以存储每个对象必须生成的唯一信息。但是,这种唯一信息只生成一次,可能会带来潜在的问题。此外,构造函数的初始化,可能会将不需要的方法放在对象上。然而,如果你只在自己的代码中使用,你也清楚(或有通过注释等写明)各段代码在做什么,这些在大体上都不是问题(事实上,通常是有益处的)。 |
Object.create | function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto = Object.create( foo.prototype );
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Copy to Clipboard function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto = Object.create( foo.prototype, { bar_prop: { value: "bar val" } } );
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop) Copy to Clipboard |
支持当前所有非微软版本或者 IE9 以上版本的浏览器。允许一次性地直接设置 __proto__ 属性,以便浏览器能更好地优化对象。同时允许通过 Object.create(null) 来创建一个没有原型的对象。 |
不支持 IE8 以下的版本。然而,随着微软不再对系统中运行的旧版本浏览器提供支持,这将不是在大多数应用中的主要问题。 另外,这个慢对象初始化在使用第二个参数的时候有可能成为一个性能黑洞,因为每个对象的描述符属性都有自己的描述对象。当以对象的格式处理成百上千的对象描述的时候,可能会造成严重的性能问题。 |
Object.setPrototypeOf | function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto = { bar_prop: "bar val" };
Object.setPrototypeOf( proto, foo.prototype );
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Copy to Clipboard function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto;
proto=Object.setPrototypeOf( { bar_prop: "bar val" }, foo.prototype );
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop) Copy to Clipboard |
支持所有现代浏览器和微软IE9+浏览器。允许动态操作对象的原型,甚至能强制给通过 Object.create(null) 创建出来的没有原型的对象添加一个原型。 |
这个方式表现并不好,应该被弃用。如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。 不支持 IE8 及以下的浏览器版本。 |
proto | function foo(){} foo.prototype = { foo_prop: "foo val" };
function bar(){} var proto = { bar_prop: "bar val", __proto__: foo.prototype };
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Copy to Clipboard var inst = { __proto__: { bar_prop: "bar val", __proto__: { foo_prop: "foo val", __proto__: Object.prototype } } };
console.log(inst.foo_prop);
console.log(inst.bar_prop) Copy to Clipboard |
支持所有现代非微软版本以及 IE11 以上版本的浏览器。将 __proto__ 设置为非对象的值会静默失败,并不会抛出错误。 |
应该完全将其抛弃因为这个行为完全不具备性能可言。 如果你在生产环境中使用这个方法,那么快速运行 Javascript 就是不可能的,因为许多浏览器优化了原型,尝试在调用实例之前猜测方法在内存中的位置,但是动态设置原型干扰了所有的优化,甚至可能使浏览器为了运行成功,使用完全未经优化的代码进行重编译。不支持 IE10 及以下的浏览器版本。 |
参考链接 【Day 33/100 JavaScript 创建对象的四种方式】https://developer.mozilla.org...
推荐阅读
- 【JS30-Wes Bos】HTML5 画板 06
- 27个精美的时间线(含源代码)
- 【JS30-Wes Bos】异步操作实现的小字典
- 获取数组嵌套深度
- 数组去重的一些方法
- JavaScript语言基础
- js基础知识小结
- 【JS30-Wes Bos】数组操作 04
- NFT 头像铸造|NFT 艺术品交易|NFT 商城开发