实例化和原型 对象实例化 协调引用 JavaScript中的每个对象,都有一个名为constructor的隐式属性,该属性引用的是创建该对象的构造器,由于prototype是构造器的一个属性,所以每个对象都有一种方式可以找到自己的原型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function Ninja ( ) { this .swung = false ; this .swingSword = function ( ) { return !this .swung; }; } Ninja.prototype.swingSword = function ( ) { return this .swung; }; var ninja = new Ninja();> ninja.swingSword ƒ () { return !this .swung; } > ninja.__proto__.swingSword ƒ () { return this .swung; } > ninja.constructor.prototype.swingSword ƒ () { return this .swung; }
通过构造器判断对象类型 1 2 3 4 5 6 7 function Ninja ( ) {}var ninja = new Ninja();console .log(ninja instanceof Ninja); console .log(ninja.constructor == Ninja); var ninja2 = new ninja.constructor();console .log(ninja2 instanceof Ninja);
不用知道原有的构造器函数也可以再次创建一个新实例,即便是原始的构造器在作用域内已经不存在了,也可以使用该引用。
继承与原型链 实现继承的最好方式,使用一个对象的实例作为另一个对象的原型:SubClass.prototype = new SuperClass();
1 2 3 4 5 6 7 8 9 10 11 12 13 function Person ( ) {}Person.prototype.dance = function ( ) {}; function Ninja ( ) {}Ninja.prototype = new Person(); var ninja = new Ninja();console .log(ninja instanceof Ninja); console .log(ninja instanceof Person); console .log(ninja instanceof Object ); console .log(typeof ninja.dance == "function" );
所有的内置对象,比如Array,包括其原型,都可以扩展它们,但不建议在原始对象上引入新的属性或方法,因为原生对象的原型只有一个实例,可能发生命名冲突
HTML DOM原型 所有的DOM元素都继承于HTMLElement构造器,通过访问HTMLElement的原型,可以提供扩展任意HTML节点的能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div id ="parent" > <div id ="a" > I'm going to be removed.</div > <div id ="b" > Me too!</div > </div > <script > HTMLElement.prototype.remove = function () { if (this .parentNode) this .parentNode.removeChild(this ); }; var a = document .getElementById("a" ); a.parentNode.removeChild(a); document .getElementById("b" ).remove(); console .log(!document .getElementById("a" )); console .log(!document .getElementById("b" )); </script >
处理HTMLElement原型时,不能在IE8之前的版本使用
扩展数字 1 2 3 4 5 6 7 8 9 10 11 Number .prototype.add = function (num ) { return this + num; }; var n = 5 ;console .log(n.add(3 ) == 8 ); console .log((5 ).add(3 ) == 8 ); console .log(5. add(3 ) == 8 ); ^^ SyntaxError : Invalid or unexpected token
报错原因,语法解析器不能处理字面量这种情况,最好避免在Number的原型做扩展
实例化问题 判断函数是否是作为构造器进行调用的 1 2 3 4 5 6 function Test ( ) { return this instanceof arguments .callee; } console .log(Test()); console .log(new Test());
通过arguments.callee可以得到当前执行函数的引用。
“普通”函数的上下文默认是全局作用域。
利用instanceof操作符测试已构建对象是否构建于指定的构造器。
在调用者上修复该问题 1 2 3 4 5 6 7 8 9 10 11 12 13 function User (first, last ) { if (!(this instanceof arguments .callee)) { return new User(first, last); } this .name = first + '' + last; } var xiaoming = new User('小' , '明' );console .log(xiaoming.name); var xiaohong = User('小' , '红' );console .log(xiaohong.name);
arguments.callee严格模式下禁用!更通用的方法如下:(摘自Effective JavaScript)
1 2 3 4 5 6 7 8 9 10 11 12 function User (first, last ) { if (!(this instanceof User)) { return new User(first, last); } this .name = first + '' + last; } var xiaoming = new User('小' , '明' );console .log(xiaoming.name); var xiaohong = User('小' , '红' );console .log(xiaohong.name);
编写类风格的代码 检测函数是否可序列化 函数序列化(function serialization)就是简单接收一个函数,然后返回该函数的源码文本。可以用这种方法检查一个函数在某一个对象中是否存在引用。在大多数浏览器中,序列化会导致函数的toString()方法会被调用。可以使用如下表达式测试一个函数是否能够被序列化。
1 /xyz/.test(function ( ) {xyz;});
子类方法 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 (function ( ) { var initializing = false , superPattern = /xyz/.test(function ( ) { xyz; }) ? /\b_super\b/ : /.*/ ; Object .subClass = function (properties ) { var _super = this .prototype; initializing = true ; var proto = new this (); initializing = false ; for (var name in properties) { proto[name] = typeof properties[name] == "function" && typeof _super[name] == "function" && superPattern.test(properties[name]) ? (function (name, fn ) { return function ( ) { var tmp = this ._super; this ._super = _super[name]; var ret = fn.apply(this , arguments ); this ._super = tmp; return ret; }; })(name, properties[name]) : properties[name]; } function Class ( ) { if (!initializing && this .init) { this .init.apply(this , arguments ); } } Class.prototype = proto; Class.constructor = Class; Class.subClass = arguments .callee; return Class; }; })();
for…in 循环中的个人理解:
要继承的属性是方法: typeof properties[name] == "function"
自己的原型链中有同名方法: typeof _super[name] == "function"
要继承的属性(方法)中含有_super
字符串: superPattern.test(properties[name])
若不能同时满足1,2,3点,则将要继承的属性作为自己实例的一个同名属性
若同时满足1,2,3点,则返回一个新的函数,该函数中调用了要继承的属性(方法),并返回调用的结果。
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 var Person = Object .subClass({ init: function (isDancing ) { this .dancing = isDancing; }, dance: function ( ) { return this .dancing; } }); var Ninja = Person.subClass({ init: function ( ) { this ._super(false ); }, dance: function ( ) { return this ._super(); }, swingSword: function ( ) { return true ; } }); var person = new Person(true );console .log(person.dance()); console .log(person instanceof Person); var ninja = new Ninja();console .log(ninja.swingSword()); console .log(ninja.dance()); console .log(ninja instanceof Ninja && ninja instanceof Person);
ES6继承 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 class Animal { constructor (name) { this .name = name; } static animalStaticMethod() { console .log('animal static method' ); } sayHi() { console .log('sayHi, I am animal ' + this .name); } } class People extends Animal { constructor (name, gender) { super (name); this .gender = gender; } sayHello() { super .sayHi(); console .log('sayHello, I am a ' + this .gender); } } Animal.animalStaticMethod(); People.animalStaticMethod(); var dog = new Animal('旺旺' );dog.sayHi(); var xiaoming = new People('小明' , 'boy' );xiaoming.sayHi(); xiaoming.sayHello();
参考自:http://es6.ruanyifeng.com/#docs/class-extends