参考
红皮书
对象属性
1.数据属性:
configurable 默认false
enumerable 默认false
writable 默认false
value 默认undefined
2.访问器属性:
configurable 默认false
enumerable 默认false
get 默认undefined
set 默认undefined
对象字面量默认都是true
3.定义多个属性Object.defineProperties
4.获取属性描述值
var prop = Object.getOwnPropertyDescriptor(book, 'year')
console.log(prop.configurable)
// 如果直接这么写,默认是采用数据属性
var book = {};
Object.defineProperties(
book,{
year:{
},
price:{
}
}
);
这里还有个细节,如果我要对一个属性设置为函数,用数据属性和访问器属性有什么区别呢,
Object.defineProperties(
book,{
page:{
// book.page会返回下面这个函数,调用book.page()会返回200
value: function(){
alert(200);
return 200;
},
}
}
);
Object.defineProperties(
book,{
page2:{
// book.page2会返回200,调用book.page2()会报错说page2不是函数
get: function(){
alert(200);
return 200;
},
}
}
);
由此可见,get是把函数直接执行,value是把整个函数返回
创建对象
1.工厂模式:
function Base(name, age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name);
};
return o;
}
var b = Base("111",20);
b instanceof Base // false
Object.getPrototypeOf(b) == Base.prototype // false
b.constructor // Object
2.构造函数模式:
function Base(name, age){
this.name = name;
this.age= age;
this.sayName = function(){
alert(this.name);
};
}
var b = new Base("111",20);
b.constructor === Base // true
b instanceof Base // true
Object.getPrototypeOf(b) === Base.prototype // true
此时new执行的操作可以理解为:
var o = {};
o.[[prototype]] = Base.prototype;
Base.call(o);
return o;
实例可以标识为特定的类型,工厂模式就不行
但是此种方式每个实例都包含各自的函数(上例中的sayName),可以这样改
function Base(name, age){
this.name = name;
this.age= age;
this.sayName = sayName;
}
function sayName(){//问题,全局
alert(this.name);
};
3.原型模式:
function Base(){}
Base.prototype = {
name : "111",
age : 20,
sayName : function(){
alert(this.name);
}
};
var b = new Base();
b.name = "222";
注意,所有函数都会有一个prototype对象,而每个prototype都会有一个constructor属性,这个属性包含一个指向 'prototype属性所在函数' 的指针
,这句话有点绕,但是表述是清晰的,举例说明,
// 注意b是函数
var b = function(){
};
typeof b.prototype === 'object' //true
// 注意prototype有一个constructor属性,不是说只有这个属性
b.prototype.constructor === b // true
当定义一个构造函数的时候,比如
function Base(){}//这步的时候
var b = new Base();
Base的prototype对象只有一个constructor属性,另外的属性都是继承Object的,
而
function Base(){}
var b = new Base();//这步的时候
b内部会有一个(内部属性)指针,指向Base.prototype(一定注意不是Base),这个指针就是[[Prototype]],但是由于是内部属性,所以如果要取出来看看怎么办呢,一些浏览器就定义了__proto__
,注意不是全部浏览器都支持
b.__proto__ === Base.prototype // true
isPrototypeOf方法和Object.getPrototypeOf方法
Base.prototype.isPrototypeOf(b) //true 沿用上面的例子
Object.getPrototypeOf(b) === Base.prototype //true
// 就可以不用__proto__了
hasOwnProperty方法可以和in一起使用来判断属性是来自实例对象自己还是来自原型对象
in可以访问原型上的属性,for...in
也是,但是如果用Object.defineProperty定义,且enumerable为false,那么就不在in里出现
如果实例对象有一个和原型对象同名的属性,delete这个属性以后,会使这个属性重新连接到原型对象上去
可以用Object.keys()函数来获取一个对象的全部可枚举
属性,不
包括原型对象上的属性,
可以用Object.getOwnPropertyNames()函数来获取一个对象的全部属性,包括不可枚举的属性,不
包括原型对象上的属性,
这里比较杂,列个表格看一下
function Base(){}
Base.prototype.name = '11';
var b = new Base();
方法 举例 作用
hasOwnProperty b.hasOwnProperty('name') 判断b是不是包含name属性,不包含Base.prototype上的属性,包含不可枚举的属性
in和for...in 'name' in b 判断b是不是包含name属性,包含Base.prototype上的属性。对于不可枚举的属性,in的时候为真,for...in中不出现
Object.keys() Object.keys(b) 获取b的全部`可枚举`属性,`不`包含Base.prototype上的属性
Object.getOwnPropertyNames() Object.getOwnPropertyNames(b) 获取b的全部属性,包含`不可枚举`的属性,`不`包含Base.prototype上的属性
之前的内容
Object.getOwnPropertyDescriptor(b, 'name') 获取b上面的name属性的属性描述
Object.getPrototypeOf(b) 获取b的原型对象
Base.prototype.isPrototypeOf(b) 判断Base.prototype是不是b的原型对象
Object.defineProperty(b,'age',{value:20}) 定义属性
Object.defineProperties
内容确实多,不要怕,一遍记不住再来一遍,不要怕反复
还没结束(姚明的笑)
当使用对象字面量的方式给Base.prototype赋值的时候,Base.prototype被重写,Base.prototype.constructor就不指向Base函数了,而是指向Object的构造函数,此时
b.constructor === Base // false
b.constructor === Object // true
如果不想出现这种情况,可以在定义的时候加上constructor: Base,
但是这样带来一个问题,此时constructor属性变成了可枚举,如果不想这样,可以用Object.defineProperty
注意以下情况
function Base(){}
var b = new Base();
b instanceof Base // true
Base.prototype = {age:20}
b instanceof Base // false
function Base(){}
Base.prototype = {age:20}
var b = new Base();
b instanceof Base // true
Base.prototype = {age:20}
b instanceof Base // false
定义实例前和定义实例后修改Base.prototype为另一个对象,情况是不同的,要警惕在定义实例之后再重写Base.prototype的情形,看个例子
function Base(){}
Base.prototype = {name:'11'}
var b = new Base();
b.name // 11
Base.prototype = {age:20}
b.name // 11 注意这里,这个时候b已经不是Base的实例了,原型对象也不是Base.prototype,但是可以取到name属性
// 因为这时候b的原型对象是第一个{name:'11'},如果做类似操作很容易出问题,也不容易查找
原型模式的缺点就不用说了,引用类型的属性的问题
4.原型模式和构造模式混合
function Base(){
this.name = '11';
}
Base.prototype.sayName = function(){
alert(this.name);
}
//或者这样,如果不想constructor可枚举可以用Object.defineProperty
Base.prototype = {
constructor: Base,
sayName: function(){
alert(this.name);
}
}
// 再举一个栗子,其实上面已经举过了
function Base(){
this.name = '11';
}
var b = new Base();
// 定义之后再改
Base.prototype = {
constructor: Base,
sayName: function(){
alert(this.name);
}
}
b instanceof Base // false
b.constructor === Base // true 注意这里,
// 如果修改Base.prototype的时候不写constructor: Base,
// 甚至把constructor改成别的函数,照样为true,
// 说明实例的constructor是new的时候就决定好了的,和prototype没关系
Object.getPrototypeOf(b) == Base.prototype // false
这里的混合主要是指把引用类型的属性放到构造函数里去,不放在原型上
5.动态原型模式
function Base(){
this.name = '11';
if(typeof this.sayName !== 'function'){
// 和混合模式的区别就是对于prototype上的属性的设置是放在构造函数里面写的
Base.prototype.sayName = function(){
alert(this.name);
}
}
}
6.寄生构造函数模式
function Base(){
var o = new Object();
o.name = '11';
return o;
}
var b = new Base();
b.constructor是Object而不是Base,要注意
b instanceof Base // false
Object.getPrototypeOf(b) == Base.prototype // false
构造函数写的和工厂模式一毛一样,但是创建实例的时候是用new的,这种写法的用处在于可以对已有的类型进行扩展,比如对Array进行扩展
和以下三种情形对比一下看,
1.构造函数返回的是Number、String、Boolean这些值类型,那么new会返回一个构造函数的实例对象,举例
function Base(){
this.name = 'base';
return 1234;//这里这个return没有什么作用,就和普通构造函数模式一样
}
var b = new Base();
b.name //base
b.constructor === Base // true
b instanceof Base // true
Object.getPrototypeOf(b) === Base.prototype // true
2.构造函数返回的是Object、Array、Function这些引用类型,那么new会返回构造函数返回的值,举例,
function Base(){
this.name = 'base';
return {
a:11
};//这种和寄生构造没有啥区别
}
var b = new Base();
b //{a:11}
b.name // undefined
b.constructor == Base // false
b instanceof Base // false
Object.getPrototypeOf(b) == Base.prototype // false
var a = new Base();
a //{a:11}
a.a = 22;
a //{a:22}
7.稳妥构造
// 一种神奇的模式,和工厂模式的区别仅仅在于,Base函数里没有使用this
function Base(name){
var o = new Object();
o.sayName = function(){
alert(name);
}
return o;
}
上面7种模式,可以分两类,一类是使用new的,一类是不使用new的(工厂和稳妥),其中工厂、原型、构造三种模式是基本模式,其余的4种都是这三种的变化
写到这里,我除了感叹一句内容真是多啊之外已经没有了任何的想法,本来还想一篇之内把构造和继承都写了,简直是痴人说梦