字面量
对象字面量 var a = {};
数组字面量 var arr = [1,2,3];
正则表达式字面量 var reg = /[a-z]/g;
javascript
属性描述符css
configurable:fasle
是一个单向操做,同时阻止使用 delete
操做符移除既存属性的能力。enumerable
控制是否在属性枚举操做中出现,好比 for..in
循环。writeable:false
时,会阻止同名属性在 [[Prototype]] 链的低层被建立(遮蔽)。不能使用 = 赋值,而必须使用 Object.defineProperty(..) writable:false
与 configurable:false
组合Object.preventExtensions(..)
Object.seal(..)
等同于 防止扩展+configurable:falseObject.freeze(..)
等同于 封印+writable:false例:建立一个可写的,可枚举的,可配置的属性pjava
var o2 = {}; Object.defineProperties(o2, { 'p': {value: 2, writable: true, enumerable: true, configurable: true }, }); Object.getOwnPropertyDescriptor( o2, "p" );
o2 = Object.create({}, { p: {value: 2, writable: true, enumerable: true, configurable: true }, }); o2.hasOwnProperty( "p" )
prototype(显式原型)
全部的函数默认都会获得一个公有的,不可枚举的属性,称为 prototype,它能够指向任意的对象。__proto__是每一个对象都有的一个属性,而prototype是函数才会有的属性。(经过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性。)git
[[Prototype]](隐式原型,非标准写法__proto__)
若是默认的 [[Get]] 操做不能直接在对象上找到被请求的属性,那么它会沿着对象的 [[Prototype]] 链 继续处理。
全部的对象(包括函数)都有一个私有属性(b.__proto__)指向它的原型对象(Foo.prototype)。该原型对象也有一个本身的原型对象(Object.__proto__) ,层层向上直到一个对象的原型对象为null。其中,下面3种隐式原型表述在本文中相同github
基本内置对象 String ,Number ,Boolean ,Object ,Function ,Array
其余内置对象 Date,RegExp,Error正则表达式
Object.getPrototypeOf(String) === Function.prototype
)typeof Xxx.prototype==='function'
的prototype。其它的构造器的prototype都是'object'.Object.prototype.constructor === Object Object.prototype.__proto__ === null Function.prototype.constructor === Function Function.prototype.__proto__ === Object.prototype
function
Foo)会建立原型对象(Object
Foo.prototype)。function Foo(){} Foo.__proto__ === Function.prototype Foo.prototype.constructor === Foo//function Foo(){} Foo.prototype.__proto__ === Object.prototype
new
会沿者 [[Prototype]] 链向上找到.constructorvar b = new Foo()//new做用:构建一个被连接到另外一个对象的对象,外加这个函数要作的其余任何事。 b.__proto__===Foo.prototype
object instanceof constructor
用于测试constructor.prototype
是否出如今object的原型链中的任何位置。数组
//像这样 object.__proto__===constructor.prototype? object.__proto__.__proto__===constructor.prototype? object.__proto__.__proto__.__proto__===constructor.prototype? ...
b instanceof Foo//true b instanceof Object//true Foo.prototype.isPrototypeOf(b)//true Object.prototype.isPrototypeOf(b)//true Foo.prototype = {}; b instanceof Foo//false b instanceof Object//true Foo.prototype.isPrototypeOf(b)//false Object.prototype.isPrototypeOf(b)//true
你们模拟了不少继承的方法,但本质上都是两种的变体:app
原型继承,本质是两个对象间创建连接,一个对象将对属性/函数的访问 委托 到另外一个对象上。
1.1使用 new函数
// 假设有一个须要继承的一个类型 Animal function Cat() {} Cat.prototype = new Animal // 添加一个属性 Cat.prototype.name = 'cat'
Bar.prototype = new Foo() 确实 建立了一个新的对象,这个新对象也的确连接到了咱们但愿的Foo.prototype。可是,它是用 Foo(..) “构造器调用”来这样作的。若是这个函数有任何反作用(好比logging,改变状态,注册其余对象,向 this添加数据属性,等等),这些反作用就会在连接时发生(并且极可能是对错误的对象!),而不是像可能但愿的那样,仅最终在 Bar()的“后裔”被建立时发生。因而,咱们剩下的选择就是使用 Object.create(..) 来制造一个新对象,这个对象被正确地连接,并且没有调用 Foo(..)时所产生的反作用。一个轻微的缺点是,咱们不得不建立新对象,并把旧的扔掉,而不是修改提供给咱们的默认既存对象。测试
1.2使用Object.create()
function Shape() {this.x = 0;this.y = 0;}// 父类 Shape.prototype.move = function(x, y) {this.x += x;this.y += y;};// 父类的方法 function Rectangle() {Shape.call(this);} //子类 Rectangle.prototype = Object.create(Shape.prototype);// 子类续承父类 Rectangle.prototype.constructor = Rectangle; var rect = new Rectangle(); rect instanceof Rectangle; // true rect instanceof Shape; // true
Object.create()作了什么?
Object.create=function (o){ function F() {} F.prototype = o; return new F(); }
两种操做将Bar.prototype 连接至 Foo.prototype:
// ES6 之前 // 扔掉默认既存的 `Bar.prototype` Bar.prototype = Object.create( Foo.prototype ); // ES6+ // 修改既存的 `Bar.prototype` Object.setPrototypeOf( Bar.prototype, Foo.prototype );
构造继承,为了符合表面上咱们用 new 调用它,并且咱们观察到它“构建”了一个对象。
本质是由于 new 让函数调用变成了“构造器调用”
// 假设有一个须要继承的一个类型 Animal function Cat(name){ Animal.call(this) // 添加一个属性 this.name = name || 'cat' }
错误方法:a instanceof Foo
instanceof 只能查询 a 的“祖先”。
勉强正确方法:用来检查 o1
是否关联到(委托至)o2
的帮助函数
function isRelatedTo(o1, o2) { function F(){} F.prototype = o2; return o1 instanceof F; }
正确方法:Foo.prototype.isPrototypeOf( a )
isPrototypeOf(..) 回答的问题是:在 a 的整个 [[Prototype]] 链中,Foo.prototype 出现过吗?
function A(name){ this.name=name; } A.prototype.sayName=function(){ console.log(this.name); } function B(age){ this.age=age; }
//原型继承 B.prototype=new A("mbj"); //被B的实例共享 var foo=new B(18); foo.age; //18,age是自己携带的属性 foo.name; //mbj,等价于foo.__proto__.name foo.sayName(); //mbj,等价于foo.__proto__.proto__.sayName() foo.toString(); //"[object Object]",等价于foo.__proto__.__proto__.__proto__.toString(); //构造函数继承
原型继承缺点:
1.全部子类共享父类实例,若是某一个子类修改了父类,其余的子类在继承的时候,会形成意想不到的后果。
2.构造子类实例的时候,不能给父类传递参数。
//构造函数继承,避免了原型继承缺点 function B(age,name){ this.age=age;A.call(this,name); } var foo=new B(18,"wmy"); foo.name; //wmy foo.age; //18 foo.sayName(); //undefined
构造函数继承缺点:
1.父类的prototype中的函数不能复用
//原型继承+构造函数继承 function B(age,name){ this.age=age;A.call(this,name); } B.prototype=new A("mbj"); var foo=new B(18,"wmy"); foo.name; //wmy foo.age; //18 foo.sayName(); //wmy
结合了上述两种方式的优势,但占用空间更大。
在 ES2015/ES6 中引入了class关键字,但只是语法糖,JavaScript 仍然是基于原型的。
注意:函数声明会提高,类声明不会。
//类声明 class Rectangle { constructor(height, width,x, y) {this.height = height,this.width = width,this.x = x,this.y = y;} get area() {return this.calcArea()} calcArea() {return this.height * this.width;} static distance(a, b) {return Math.hypot(a.x - b.x, a.y - b.y);} } const square1 = new Rectangle(10, 10,5,5); const square2 = new Rectangle(10, 10,6,6); console.log(square1.area); console.log(Rectangle.distance(square1 , square2 )); //类表达式 let Rectangle = class Rectangle {constructor() {}};
extend 实现继承
class Rectangle extends Shape { move() { super.move();//super 关键字用于调用对象的父对象上的函数 } }
常规(非可构造)对象 实现继承
var Animal = {}; class Cat {} Object.setPrototypeOf(Cat.prototype, Animal);
混入(Mixin)
Object.assign(target, ...sources) 方法只会拷贝源对象(...sources)自身的而且可枚举的属性到目标对象(target)。
function MyClass() { SuperClass.call(this) OtherSuperClass.call(this) } // 继承一个类 MyClass.prototype = Object.create(SuperClass.prototype) // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype) //Object.assign 会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上, //使 MyClass 的全部实例均可用 OtherSuperClass 的方法 // 从新指定constructor MyClass.prototype.constructor = MyClass MyClass.prototype.myMethod = function() {}// do a thing
当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。
var o = { a: 2, m: function(){ return this.a + 1; } }; var p = Object.create(o); // p是一个继承自 o 的对象 p.a = 4; // 建立 p 的自身属性 a console.log(p.m()); // 5
派生
class MyArray extends Array { static get [Symbol.species]() { return Array; } }
var Widget = { init: function(width,height){ this.width = width || 50; this.height = height || 50; this.$elem = null; }, insert: function($where){ if (this.$elem) { this.$elem.css( { width: this.width + "px", height: this.height + "px" } ).appendTo( $where ); } } }; var Button = Object.create( Widget ); Button.setup = function(width,height,label){ // delegated call this.init( width, height ); this.label = label || "Default"; this.$elem = $( "<button>" ).text( this.label ); }; Button.build = function($where) { // delegated call this.insert( $where ); this.$elem.click( this.onClick.bind( this ) ); }; Button.onClick = function(evt) { console.log( "Button '" + this.label + "' clicked!" ); }; $( document ).ready( function(){ var $body = $( document.body ); var btn1 = Object.create( Button ); btn1.setup( 125, 30, "Hello" ); var btn2 = Object.create( Button ); btn2.setup( 150, 40, "World" ); btn1.build( $body ); btn2.build( $body ); } );
Object.create 和 new 区别
js中__proto__和prototype的区别和关系?
你不懂JS: this 与对象原型
行为委托