理解对象

博客分类: 原创

理解对象

对象属性类型:数据属性访问器属性

数据属性(EC5)

  • [[Configurable]]:能否把属性修改为访问器属性。默认值为true
  • [[Enumerable]]:能否通过for-in遍历到。默认值为true
  • [[Writable]]:能否修改属性值。默认值为true
  • [[Value]]:值。默认值为undefined
var personA = {
    name : 'Karyn'
};

var personB = {};
Object.defineProperty(personB, "name", {
    configurable : false,
    enumerable : false,
    writable : false,
    value : "Karyn"
});

// 一看两种声明都是一样的,可对应这我们上面的数据属性验证一下。
console.log(personA)     // {name: "Karyn"}
console.log(personB)     // {name: "Karyn"}

// 修改configurable 和 enumerable 会报错
Object.defineProperty(personB, "name", {
    configurable : true
});

// 通过 for-in 不能遍历出来
for(var x in personB){
    console.log(x);     // undefined
}

// 值无法改变
personB.name = "song";
console.log(personB.name)            // karyn

访问器属性

  • [[Configurable]]:能否把属性修改为访问器属性。默认值为true
  • [[Enumerable]]:能否通过for-in遍历到。默认值为true
  • [[Get]]:在读取该属性值时调用的函数。默认值为undefined
  • [[Set]]:在写入属性时调用的函数。默认值为undefined
var person = {
    _name : "karyn",    // 通常我们用下划线开头来表示私有变量
    edition : 1
}

Object.defineProperty(person, "name", {
    get: function(){
        return this._name;
    },
    set: function(newValue){
        this._name = newValue;
        this.edition++;
    }
});
// person = {_name : "karyn", edition : 1, name : "karyn"}

// 我们看到_表示私有,但这里是特定的语法,我们看看换成其他符号呢
var person = {
    $name : "karyn",    // 通常我们用下划线开头来表示私有变量
    edition : 1
}

Object.defineProperty(person, "name", {
    get: function(){
        console.log("get",this.$name,this.name);
    },
    set: function(newValue){
        console.log("set",newValue);
    }
});

// person = {_name : "karyn", edition : 1, name : [Exception: RangeError: Maximum call stack size exceeded]}

var person = {
    name : "karyn",    // 通常我们用下划线开头来表示私有变量
    edition : 1
}

Object.defineProperty(person, "name", {
    get: function(){
        console.log("get",this.name);
    },
    set: function(newValue){
        console.log("set",newValue);
    }
});

// person = {edition : 1, name : [Exception: RangeError: Maximum call stack size exceeded]}

person.name = "song"
console.log(person.name)             // song
console.log(person.edition)          // 2

// 新的写法
var person = {
    $name : "karyn",
    edition : 1,
    get name(){
        console.log("get",this.$name);
    },
    set name(value){
        this.$name = value;
        this.edition++;
        console.log("set",this.$name)
    }
}

// 以上写法在更早之前可以写成以下方法
Object.__defineGetter__("name", function(){
    return this._name;
});

Object.__defineSetter__("name", function(newValue){
    this._name = newValue;
    this.edition++;
});

读取属性的特性

// 通过 getOwnPropertyDescriptor 来读取私有字段
var descriptor = Object.getOwnPropertyDescriptor(person, "_name");

工厂模式与构造函数模式

检验方式可以通过constructorinstanceof来实现。但是还是有区别的。

function Person(){}
var person = new Person();

console.log(person.constructor === Person)      // true
console.log(person.constructor === Object)      // false
console.log(person instanceof Object)           // true
console.log(person instanceof Object)           // true

原型对象

var Person = function(){
    this.name = "karyn";
}
Person.prototype = {
    name: "song",
    age: 11,
    sayName: function(){
        console.log(this.name)
    }
}

var person = new Person();

// 在设计之初是不允许直接访问原型链的
// 可以用 isPrototypeOf 来测试 person 的原型链
console.log(Person.prototyoe.isPrototypeOf(person))

// EC5 增加了方法可以直接获取 Person.prototype
console.log(Object.getPrototypeOf(person))

// 也可以通过 __proto__ 访问
person.__proto__ === Person.prototype

// 这里有三个角色
构造函数:Person
构造函数的原型:简称 PerPro
实例:person

// 关系
Person.prototype = PerPro;
person.__proto__ = PerPro;
PerPro.constructor = Person;

// 基于上面这种关系 person 在想要调用 name 的属性先要访问 Person ,Person 有该属性则返回,且这个时候 PerPro 上的 name 是没有被覆盖的,如果要访问 age,则需要先访问 Person,发现 Person 没有,则去访问 PerPro,发现有则返回。
// in 方法能检验出属性是来自于构造器还是原型链
// 所以使用 for-in 来遍历会遍历出原型链的方法
// EC5 的 key 和 getOwnPropertyNames 可以达到同样的效果
var keys = Object.getOwnPropertyNames(Person.prototype);    // ['name', 'age', 'sayName']

// 检验取出来的值是来自于对象实例还是原型可以通过 hasOwnProperty 来检验