js的对象、构造

参考
红皮书

对象属性

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上的属性,包含不可枚举的属性
infor...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种都是这三种的变化

写到这里,我除了感叹一句内容真是多啊之外已经没有了任何的想法,本来还想一篇之内把构造和继承都写了,简直是痴人说梦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值