对象的属性类型包含:数据属性、访问器属性javascript
数据属性vue
访问器属性java
没有value和writable,多了一对get和set。数组
无论writable真假,只要configurable为真,经过Object.defineProperty就能把特性改成访问器属性。浏览器
Object.getOwnPropertyDescriptor(o,”prop”);
:读取属性特性。安全
Object.getOwnPropertyDescriptors(o);
:读取全部属性特性。闭包
犀牛书里把访问器accessor称为存取器,我以为这样更形象。可使用直接量语法的扩展语法来定义属性,好比下面两种方法:app
var o={
name:'tiedan',
get ga(){return this.name},
set ga(value){this.name=value}
}
var p={name:'tiedan'};
Object.defineProperty(p,'ga',{
set:function(value){this.name},
get:function (){return this.name}});
console.log(Object.getOwnPropertyDescriptors(o));
console.log(Object.getOwnPropertyDescriptors(p));
/*{ name:{ value: 'tiedan',writable: true,enumerable: true,configurable: true }, ga:{ get: [Function: get ga],set: [Function: set ga],enumerable: true,configurable: true } } { name:{ value: 'tiedan',writable: true,enumerable: true,configurable: true }, ga:{ get: [Function: get],set: [Function: set],enumerable: false,configurable: false } }*/
//方括号里有细微区别。
复制代码
使用Object.defineProperty
或者Ojbect.defineProperties
,不显示定义configurable、writable、enumerable,则这三个特性默认值都为false函数
vue的核心实现就是利用
Object.definProperty
劫持属性的get和set特性,来实现双向绑定的。测试
Object.preventExtensions
对象不能添加新属性(可删除原有属性)。可经过Object.isExtensible来检测对象是否能扩展。
Ojbect.seal
能够密封对象,密封后的对象不能删除和添加属性。全部属性特性的[[configurable]]都为false。可经过Object.isSeal来检测对象是否为密封对象。
Object.freeze
能够冻结对象,比Object.seal更进一步,对象不能有任何变化。全部特性均为false。可经过Object.isFrozen来检测对象是否为冻结对象。
经过Object.getOwnPropertyNames
看到的Object.prototype的属性,这部分属性都是可继承的。
[ 'constructor', //给全部对象的构造器属性,建立函数时,就会自动建立函数原型,和其constructor属性,若是重写原型,则继承这个
'__defineGetter__', //一些浏览器的非标准方法,在Object.defineProperty不支持的时候,能够尝试用这个定义属性的特性。
'__defineSetter__', //同上相似
'hasOwnProperty', //检测对象是否有自有的属性
'__lookupGetter__', //非标准方法,用来返回命名属性的Getter方法
'__lookupSetter__', //同上相似
'isPrototypeOf', //是否为检测对象的原型。其原理同instanceof同样都是查找原型链,只是instanceof后面是构造函数
'propertyIsEnumerable',//属性是不是可枚举的,这个和Object.getOwnPropertyDescriptor()里获得的enumberable属性是同样的
'toString', //
'valueOf', //
'__proto__', //通常是内部属性,一个原型指针
'toLocaleString' ] //
复制代码
也能够经过Object.getOwnPropertyNames
看看Object的属性和方法。
犀牛书例子,最早由道格拉斯.克罗克福德提出,这我的在两本书里出现N屡次。下面的是犀牛书p122的例子,红皮书相似的例子在p169。 Object.create()
建立一个新对象,参数是新对象的原型。create方法有两个参数,第二个参数与defineProperties的第二个参数同样。
function inherit(p){
if(p==null) throw TypeError();
//首先排除null,自己typeof就可能有null。Object.create是能够传null的
if(Object.create){
return Object.create(p);
}
var t=typeof p;
if(t!="object"&&t!="function")throw TypeError;//不能是基本类型值
function a(){}
a.prototype=p;
return new a();
}
复制代码
new的实现大体以下:
o = {};
o.__proto__=f.prototype;
f.apply(o,arguments);
复制代码
因此new Object();
不如直接用{}
执行快就是由于这个缘由吗?
自定义的new以下:
function New(f) {
//返回一个func
return function () {
o = {};
o.__proto__=f.prototype;
f.apply(o, arguments);//继承父类的属性
return o; //返回一个Object
}
}
复制代码
经过new fn();
或者Object.create(p);
其实都是返回新对象的方法。
“生产对象”,因此通常直接调用。不使用new(使用也能返回正常对象)。
function Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
return o;
}
复制代码
弊端:解决了对象建立问题,但没有解决对象识别问题
function Person(name,age){
this.name=name;
this.age=age;
}
复制代码
没有显示建立对象,直接将属性和方法赋给this,(能够)没有return。经过new来调用。
实际上步骤:
var o={};//建立新对象
o.__proto__=fn.prototype;//对象的原型指针赋值
fn.apply(o,arguments);//this指向新对象,执行构造函数代码
return o;//返回新对象
复制代码
new fn()
就是相似上面的过程。构造函数fn的执行实际上就是给o对象赋值操做的过程。
弊端:方法重复定义,即使放到全局也只适合对象调用,还容易污染全局做用域。
function Person(){
}
Person.prototype.name='tiedan';
Person.prototype.age='1';
复制代码
对于原型有两种写法:
fn.prototype.p1=value1;
fn.protptype.p2=value2;
复制代码
fn.prototype={
p1:value1,
p2:value2
}
复制代码
第二种看起来更清楚,可是存在问题,由于这至关于重写原型对象。以前定义好的一些属性方法可能会丢失,constructor属性须要显示从新指向构造函数。要不就没了。在第二种重写原型以前不能先new fn,这会致使重写原型以后切断了构造函数fn和最初的原型之间的联系。由于prototype属性自己存储的就是一个指针。而对象和原型之间的联系仅靠__proto__存储的一个指针。至关于原型链出了问题,没法经过这个属性找到新的原型对象,得到新对象的属性和方法。说白了就是fn和obj指向了不一样的原型对象。
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.height=100;
var p1=new Person("tiedan",36);
Person.prototype={
constructor:Person,
height:180
}
var p2=new Person("someone",29);
console.log(p1 instanceof Person);//false,由于其__proto__指向的已经不是最新的Person.prototype。经管如此,p1.constructor.prototype仍是对的。
console.log(p1.height);//100
console.log(p2 instanceof Person);//false
console.log(p2.height);//180
复制代码
弊端:除了上面所说的,还有就是由于原型对象的属性和方法都是实例共享的。那么对于属性是引用类型,好比数组,你们也是共享的同一个引用对象。修改这类属性值,实际上会影响到全部实例。
建议属性值是引用类型的,不要用原型模式,除非你就须要这种共享引用类型的方式。
in
运算符有两种用法,一种用于for...in。另外一种在单独使用时,in
操做符会在经过对象可以访问给定属性时返回true,不管该属性存在于实例中仍是原型中!
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
复制代码
function Person(name,age){
this.name=name;
this.age=age;
if(typeof this.sayName!='function'){
Person.prototype.sayName=function(){console.log(this.name);}
}
}
复制代码
相对比较完美的一种模式。
使用这种模式,一样不能用对象字面量重写原型。思考一下new的实现步骤,就能明白,构造函数里重写原型,则必然会在o对象建立以后切断与原有原型的联系。
function Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
return o;
}
var tiedan=new Person('铁蛋',1);
console.log(tiedan instanceof Person);//false
复制代码
这个模式和工厂模式的函数彻底同样,只是这个直接经过new来调用。测试了一下,当作构造函数使用,末尾return会代替构造函数正常的return值。
弊端:返回的对象和构造函数没什么关系。不能依赖instanceof来检测对象。(返回的其实是里面的o类型)
durable object持久对象 克罗克福德发明
function Person(name,age){
var o=new Object();
o.sayName=function(){
alert(name);
};
return o;
}
var tiedan=Person('铁蛋');
复制代码
相似于寄生构造函数模式。没有公共属性,不引用this对象。适合在安全环境或防止数据被其余应用程序改动时使用。1.不引this,2.不用new
实际上是利用了闭包原理。即使对象被添加属性和方法,也没法篡改原始值。
继承分为接口继承和实现继承
红皮书p162说es中没法实现接口继承,其实es4版本是实现了接口继承的(as3.0)。说不定哪天interface就从保留字变关键字了。
别忘记默认原型:Object.prototype
肯定原型和实例的关系,instanceof
,isPropertyOf
谨慎的定义方法,方法覆盖,红皮书p166必须用SuperType实例替换SubType的原型后再定义原型方法,不然原型对象一重写就没了。
原型链的问题:
相似以前原型模式引用类型的问题。SubType.prototype=new SuperType();
实际上就是重写原型。原型属性包含引用类型就容易出现意料以外的状况。
constructor stealing 明明是偷非说是借~
function SubType(age){
SuperType.call(this,"tiedan");
this.age=age;
}
复制代码
将借用构造函数和原型链技术结合
借用构造函数获得实例属性
原型链技术继承原型属性和方法,同时还能够扩充本身的原型方法
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.prop=…
复制代码
又是道格拉斯.克罗克福德... 提到了object(o)函数,犀牛书里名为inherit(p)
var person={};
var anotherPerson=Object.create(person);
anotherPerson.prop=...
//继承的引用类型会被实例共享
复制代码
Object.create在使用一个参数的时候,行为和object(o);相同,使用两个参数的时候,第二个参数和Object.defineProperties的第二个参数格式相同。以这种方式指定的属性会覆盖原型对象的同名属性。
其实inherit也能够加第二个参数,也就是作一个浅复制,这能够参看犀牛书的浅复制。extend方法。
var p2=Object.create({},{"hehe":{value:30 }});
console.log(p2);//{}
//什么都看不见,由于和Object.defineProperties同样,enumerable不显示定义默认为false,因此看不到。
复制代码
克罗克福德推广的
function createAnother(o){
var anotherPerson=Object.create(o);
anotherPerson.sayHello=function(){console.log('hello');}
return anotherPerson;
}
var anotherPerson=createAnother({});
anotherPerson.sayHello();
复制代码
主要考虑对象而不是自定义类型和构造函数的状况下使用
寄生式继承的思路与寄生构造函数和工厂模式相似。建立一个用来封装继承过程的函数。在函数内部以某种方式加强对象,最后像本身作了全部工做同样返回对象。
以前的组合式继承最大的问题是要调用两次父类构造函数,一次是借用构造函数,一次是重写子类原型。其实不必调用两次构造函数。能够只调用一次借用构造函数,另外一次寄生式new一个空构造函数。只为了复制父类原型属性和方法。
function SuperType(name){
this.name=name;
this.colors=['red','green','blue'];
}
SuperType.prototype.sayName=function(){console.log(this.name);}
function subType(name,age){
SuperType.call(this,name);//第二次调用
this.age=age;
}
SubType.prototype=new SuperType();//第一次调用
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){console.log(this.age);}
复制代码
改成:
function inheritPrototype(SubType,SuperType){
var prototype=Object.create(SuperType.prototype);//建立对象
prototype.constructor=SubType;//加强对象
SubType.prototype=prototype;//指定对象,依然重写原型啊...
}
function SuperType(name){
this.name=name;
this.colors=['red','green','blue'];
}
SuperType.prototype.sayName=function(){console.log(this.name);}
function SubType(name,age){
SuperType.call(this,name);//第二次调用
this.age=age;
}
inheritPrototype(SubType,SuperType);//第一次调用
SubType.prototype.sayAge=function(){console.log(this.age);}
复制代码
上面例子依然重写了原型啊。因此,顺序很重要。不然,最后两行换一下位置,sayAge就被盖掉了。