什么是跨浏览器广泛支持的最安全的方法来创建原型链?

时间:2016-11-27 13:37:47

标签: javascript inheritance

所以我还在学习JavaScript,而且我现在正在使用原型。我刚刚完成的课程向我展示了如何使用__proto__创建原型继承链。这是我的意思的一个例子:

function Person(firstName,lastName,dob){
  this.firstName = firstName;
  this.lastName = lastName;
  this.dob = new Date(dob);
}
Person.prototype = {
  constructor: Person,
  getFullName: function(){
    return this.firstName + " " + this.lastName;
  },
  getAge: function(){
    var ageDiff = Date.now() - this.dob;
    var ageDiffDate = new Date(ageDiff);
    return Math.abs(ageDiffDate.getUTCFullYear() - 1970);
  }
}

function Employee(firstName,lastName,dob,position,dept){
  this.firstName = firstName;
  this.lastName = lastName;
  this.dob = new Date(dob);
  this.position = position;
  this.dept = dept;
  this.isFired = false;
}
Employee.prototype = {
  constructor: Employee,
  __proto__: Person.prototype,
  doWork: function(){
    console.log(this.getFullName() + " is doing work");
  }
}

function Manager(firstName,lastName,dob,dept){
  this.firstName = firstName;
  this.lastName = lastName;
  this.dob = new Date(dob);
  this.position = "Manager";
  this.dept = dept;
}
Manager.prototype = {
  constructor: Manager,
  __proto__: Employee.prototype,
  fireEmployee: function(emp){
    emp.isFired = true;
    console.log(this.getFullName() + " has fired " + emp.getFullName());
  }
}

var per = new Person("Bob","Saget","1990-02-05");
var emp = new Employee("Jane","Doe","1980-05-02","Clerk","Sales");
var mgr = new Manager("Jim","Smith","1970-09-10","Sales");

Employee继承自Person.prototypeManager继承自Employee.prototype。好又简单。

但是,我已经读过所有网络浏览器都不保证__proto__属性,因为ES6还没有被广泛采用。我一直在扫描互联网,试图找到一个明确的解释,该怎么做,我有点迷失。

除了使用__proto__之外,在ES5中设置原型继承链的安全,广泛支持的方法是什么?

2 个答案:

答案 0 :(得分:1)

最安全的方法是使用标准 ES5及以上方法。

function A() {}

function B() {}
B.prototype = Object.create(A.prototype);

答案 1 :(得分:0)

引自MDN

  

使用__proto__是有争议的,并且不鼓励。它最初从未包含在EcmaScript语言规范中,但现代浏览器决定实现它。直到最近,__ proto__属性已经在ECMAScript 6语言规范中针对Web浏览器进行了标准化,以确保兼容性,因此将来会得到支持。它已被弃用,有利于Object.getPrototypeOf / Reflect.getPrototypeOfObject.setPrototypeOf / Reflect.setPrototypeOf(尽管如此,设置对象的[[Prototype]]是一个缓慢的操作,应该是如果表现是一个问题就避免了。)

请同时考虑有关表现的警告

  

警告:根据现代JavaScript引擎优化属性访问的性质,在每个浏览器和JavaScript引擎中,更改对象的[[Prototype]]是非常慢的操作。 (...)

如果您不介意性能影响,或者只是想尝试原型,那么文章Inheritance and the prototype chain就JS中的原型链如何工作提供了一个很好的介绍。我将在这里引用提到创建自定义原型链的三种不同方法的部分:

使用构造函数

A"构造函数"在JavaScript中只是"只是"恰好用new operator调用的函数。



function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

var g = new Graph();

console.log("g:", Object.keys(g))
console.log("g.prototype:", Object.keys(Object.getPrototypeOf(g)))




使用Object.create

ECMAScript 5引入了一种新方法:Object.create()。调用此方法会创建一个新对象。该对象的原型是函数的第一个参数:



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

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)

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

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); 
// undefined, because d doesn't inherit from Object.prototype




使用class关键字

ECMAScript 6引入了一组实现类的新关键字。虽然这些结构看起来像基于类的语言开发人员熟悉的结构,但它们并不相同。 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);




结论

我同意Matias'回答并建议使用Object.create,因为它不会隐藏JavaScript的原型性质 - 与构造函数或ES6类相比。