主要有:new、Object.create()、Object.setPrototypeOf()、instanceof、class、type API、原型链继承等。
new 实现 我们看下 new 做了什么:
创建一个新对象;
将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
执行构造函数中的代码(为这个新对象添加属性)
返回新对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function objectFactory ( ) { const obj = new Object (); const Constructor = [].shift.call(arguments ); obj.__proto__ = Constructor.prototype; const ret = Constructor.apply(obj, arguments ); return typeof ret === "object" ? ret : obj; }
ES6:
1 2 3 4 5 6 7 8 9 function createNew (Con, ...args ) { this .obj = {}; this .obj = Object .create(Con.prototype); const ret = Con.apply(this .obj, args); return ret instanceof Object ? ret : this .obj; }
Object.create 实现 Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。
使用 Object.create 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 var o;o = Object .create(null ); o = {}; o = Object .create(Object .prototype); function Constructor ( ) {}o = new Constructor(); o = Object .create(Constructor.prototype); Object .setPrototypeOf(o, Constructor.prototype);o.__proto__ === Constructor.prototype); o = Object .create(Object .prototype, { foo: { writable: true , configurable: true , value: "hello" }, bar: { configurable: false , get: function ( ) { return 10 ; }, set: function (value ) { console .log("Setting `o.bar` to" , value); } } });
模拟 Object.create 实现原理 采用了原型式继承 :将传入的对象作为创建的对象的原型。
1 2 3 4 5 Object .mycreate = function (proto ) { function F ( ) {} F.prototype = proto; return new F(); };
详细 Polyfill,见 MDN:Object.create() ,其实就多了参数的判断等信息。
Object.setPrototypeOf 实现 Object.setPrototypeOf() 方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]
属性)到另一个对象或 null
。
注意 :由于性能问题,你应该使用 Object.create() 来创建带有你想要的[[Prototype]]
的新对象。详情见:MDN 。
使用较旧的 Object.prototype.__proto__
属性,我们可以很容易地定义 setPrototypeOf
:
1 2 3 4 5 6 7 Object .setPrototypeOf = Object .setPrototypeOf || function (obj, proto ) { obj.__proto__ = proto; return obj; };
instanceof 实现 用法 :instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
原理 :其实就是沿着原型链一直询问,直到__proto__
为 null
为止。
注意 :Object.prototype.isPrototypeOf()
用于测试一个对象是否存在于另一个对象的原型链上。isPrototypeOf()
与 instanceof
运算符不同。在表达式 “object instanceof AFunction
“中,object 的原型链是针对 AFunction.prototype
进行检查的,而不是针对 AFunction
本身。
如果你有段代码只在需要操作继承自一个特定的原型链的对象的情况下执行,同 instanceof
操作符一样 isPrototypeOf()
方法就会派上用场:
1 2 3 4 5 6 7 8 9 10 11 function Car ( ) {}var mycar = new Car();var a = mycar instanceof Car; var b = mycar instanceof Object ; var aa = Car.prototype.isPrototypeOf(mycar); var bb = Object .prototype.isPrototypeOf(mycar);
要检测对象不是某个构造函数的实例时,你可以这样做:
1 2 3 4 5 6 if (!(mycar instanceof Car)) { } if (!Car.prototype.isPrototypeOf(mycar)) { }
instanceof 模拟实现 :主要是沿着__proto__
判断:L.__proto__
是否等于R.prototype
:
1 2 3 4 5 6 7 8 9 10 11 12 function myinstanceof (L, R ) { const O = R.prototype; L = L.__proto__; while (true ) { if (L === null ) return false ; if (O === L) return true ; L = L.__proto__; } } var a = myinstanceof(mycar, Car); var b = myinstanceof(mycar, Object );
原型链继承实现 用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Parent (name ) { this .name = name; } Parent.prototype.getName = function ( ) { return this .name; }; function Child (name, age ) { Parent.call(this , name); this .age = age; } Child.prototype = Object .create(Parent.prototype); Child.prototype.constructor = Child; Child.prototype.getMsg = function ( ) { return `My name is ${this .name} , ${this .age} years old.` ; };
ES6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Parent { constructor (name) { this .name = name; } getName() { return this .name; } } class Child extends Parent { constructor (name, age) { super (name); this .age = age; } getMsg() { return `My name is ${this .name} , ${this .age} years old.` ; } }
封装继承方法 第一版:
1 2 3 4 5 6 function prototype (child, parent ) { const F = function ( ) {}; F.prototype = parent.prototype; child.prototype = new F(); child.prototype.constructor = child; }
第二版:
1 2 3 4 5 6 7 8 9 10 function create (o ) { const F = function ( ) {}; F.prototype = o; return new F(); } function prototype (child, parent ) { child.prototype = create(parent.prototype); child.prototype.constructor = child; }
第三版:
1 2 3 4 function prototype (child, parent ) { child.prototype = Object .create(parent.prototype); child.prototype.constructor = child; }
使用到的几种继承方式 组合式继承 :融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。组合继承最大的缺点是会调用两次父构造函数。
原型式继承 :Object.create
的模拟实现,将传入的对象作为创建的对象的原型。
寄生组合式继承 :只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。
PS:其他几种继承方式见这里JavaScript 深入之继承的多种方式和优缺点 。
引用《JavaScript 高级程序设计》中对寄生组合式继承 的夸赞就是:
这种方式的高效率体现它只调用了一次 Parent
构造函数,并且因此避免了在 Parent.prototype
上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof
和 isPrototypeOf
。开发人员普遍认为寄生组合式继承是引用类型 最理想的继承范式。
class 实现 主要是模拟使用extends
,并模拟super
可以给其父构造函数传值,如 Parent 中的 opt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Parent { constructor (opt) { this .name = opt.name; } getName() { return this .name; } } class Child extends Parent { constructor (opt) { super (opt); this .age = opt.age; } getAge() { return this .age + " years old." ; }; } const me = new Child({ name : "Yang" , age : 28 });
开始模拟实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 function _extends (child, parent ) { child.prototype = Object .create(parent && parent.prototype); child.prototype.constructor = child; Object .setPrototypeOf ? Object .setPrototypeOf(child, parent) : (child.__proto__ = parent); } function _classCallCheck (instance, Constructor ) { if (!(instance instanceof Constructor)) { throw new TypeError ("Cannot call a class as a function" ); } } var Parent = (function ( ) { function Parent (opt ) { _classCallCheck(this , Parent); this .name = opt.name; } Parent.prototype.getName = function getName ( ) { return this .name; }; return Parent; })(); var Child = (function (_Parent ) { _extends(Child, _Parent); function Child (opt ) { _classCallCheck(this , Child); var _this = (_Parent != null && _Parent.call(this , opt)) || this ; _this.age = opt.age; return _this; } Child.prototype.getAge = function getAge ( ) { return this .age + " years old." ; }; return Child; })(Parent); const myself = new Child({ name : "YyY" , age : 18 });
附加两篇文章:
Array.isArray 实现 可以通过 toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString()
来检测,需要以 Function.prototype.call()
或者 Function.prototype.apply()
的形式来调用,传递要检查的对象作为第一个参数。
1 2 3 4 Array .myIsArray = function (o ) { return Object .prototype.toString.call(o) === "[object Array]" ; }; console .log(Array .myIsArray([]));
type API 实现 写一个 type 函数能检测各种类型的值,如果是基本类型,就使用 typeof
,引用类型就使用 toString
。
此外鉴于 typeof
的结果是小写,我也希望所有的结果都是小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var class2type = {};"Boolean Number String Function Array Date RegExp Object Error Null Undefined Symbol Set Map BigInt" .split(" " ) .map(function (item ) { class2type["[object " + item + "]" ] = item.toLowerCase(); }); function type (obj ) { if (obj == null ) { return obj + "" ; } return typeof obj === "object" || typeof obj === "function" ? class2type[Object .prototype.toString.call(obj)] : typeof obj; } function isFunction (obj ) { return type(obj) === "function" ; } var isArray = Array .isArray || function (obj ) { return type(obj) === "array" ; };
参考资料:JavaScript 专题之类型判断(上)
参考资料