您的位置:首页 > Web前端 > JavaScript

js高级程序设计笔记 -- 属性类型以及创建对象

2017-03-13 21:55 716 查看

1.属性类型

ECMAScript中有两种属性:数据属性和访问器属性

1.数据属性:

数据属性包含一个数据值的位置,在这个位置可以读取和写入值
[[ Configurable ]] : 表示能否删除属性或者修改属性为访问器属性。默认值为true;
[[ Enumerable ]] : 表示能否通过for-in 循环返回属性。默认值为true;
[[ Writable ]] : 表示能否修改属性的值 。默认值为true;
[[ Value ]] : 包含这个属性的数据值,读取属性值的时候从这个位置读取,写入属性值的时候,把新值保存在这个位置 默认值为 undefined;

修改数据属性默认的特性,必须使用 ECMAScript5 的object.defineProperty() 方法。这个方法接受三个参数:
属性所在的对象,属性的名字和一个描述符对象,其中描述符对象的属性必须是: configurable, enumerable, writable, value.

var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "mnixu"
})

console.log(person.name) //"mnixu"
person.name = "xu"
console.log(person.name) //"mnixu"

var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "mnixu"
})
console.log(person.name) //"mnixu"
delete person.value;
console.log(person.name) //"mnixu"

var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "mnixu"
})
//拋出錯誤
Object.defineProperty(person, "name", {
configurable: true,
value: "mnixu"
})

注意:一但把属性定义为不可配置的,就不能再把它变为可配置的。
在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable, enumerable, writable,特性的默认值都为false,
如果调用Object.defineProperty()方法只是修改已定义的属性,则无限制
 

2.访问器属性:

访问器属性不包含数据值:他们包含一对getter和setter函数。
也有四个特性。
[[ Configurable ]] : 表示能否删除属性或者修改属性为访问器属性。默认值为true;
[[ Enumerable ]] : 表示能否通过for-in 循环返回属性。默认值为true;
[[ get ]] : 在读取属性时调用的函数。默认值为undefined;
[[ set ]] : 在写入属性是调用的函数。 默认值为undefined;

访问器属性不能直接定义,必须 使用object.defineProperty() 方法 来定义

var book = {
_year: 2004,
edition: 1
}
Object.defineProperty(book, "year", {
get: function () {
return this._year;
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004
}
}
})

book.year = 2005;
console.log(book.edition)


_year前面的下划线是用于表示只能通过对象方法访问的属性。
getter函数返回_year的值,setter函数通过计算来确定正确的版本,修改year属性为2005时候会导致_year变为2005
edition变为2,这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。

2.定义多个属性

 使用Object.defineProperties()方法可以定义多个属性

var book = {};
Object.defineProperties(book, {
_year: {
writable: true,
value: 2004
},
edition: {
writable: true,
value: 1
},
year: {
get: function () {
return this._year;
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004
}
}
}
});


这个定义的最终对象和上面定义的对象相同,唯一的区别是这里的属性都是在同一时间创建的。

3.读取属性的特性

使用Object.getOwnPropertyDescriptor() 可以取得给定义属性的描述符
例子:
var descriptor = Object.getOwnPropertyDescriptor(book, "_year")
console.log(descriptor.value) //2004

var descriptor = Object.getOwnPropertyDescriptor(book, "year")
console.log(descriptor.enumerable)  //false
console.log(typeof descriptor.get)  //"function"

2.创建对象

1.构造函数模式

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name)
};
}

var person1 = new Person("mnixu", "20", "Student");
var person2 = new Person("zhangsan", "11", "Student");
//person1 和 person2 都保存着Person 的不同实例。都有一个constructor(构造函数)属性,该属性指向Person
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true

//对象的constructor属性最初使用来标识对象类型的。但是,检测对象类型使用instanceof操作符更可靠
console.log(person1 instanceof Person); //true
console.log(person1 instanceof Object); //true
console.log(person2 instanceof Person); //true
console.log(person2 instanceof Object); //true


构造函数也可以当做普通函数来调用:
//当做构造函数使用
var person = new Person("mnixu", "20", "Student");
person.sayName(); //"mnixu"

//当做普通函数使用
Person("zhangsan", "20", "Student"); //添加到window
window.sayName(); //"zhangsan"

//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "lisi", "20", "Student");
o.sayName(); //"lisi"


构造函数模式虽然好用,但是使用构造函数有一个主要问题,就是每个方法都要在每个实例上重新创建一遍。
前面的person1 和 person2 都有一个sayName()方法,但是两个方法不是同一个Function实例。
在ECMAScript中的函数就是对象,每定义一个函数,就是实例化一个对象。
console.log(person1.sayName == person1.sayName); //false

2.原型模式

我们创建的每一个函数都有一个prototype(原型)属性。这个属性是一个指针,指向一个对象。
使用原型对象可以让所有对象实例共享它所包含的属性和方法。
function Person() { };

Person.prototype.name = "mnixu";
Person.prototype.age = "20";
Person.prototype.job = "Student";
Person.prototype.sayName = function () {
console.log(this.name);
};

var person1 = new Person();
person1.sayName(); //"mnixu"
var person2 = new Person();
person2.sayName(); //"mnixu"

console.log(person1.sayName == person1.sayName); //true
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其他方法,都是从Object继承过来的。
当我们调用构造函数创建一个新的实例后,该实例内部讲包含一个指针(内部属性),指向构造函数的原型对象。
ES5管这个指针叫[[ Prototype ]] 。我们可以通过__proto__这个内置属性来访问
console.log(person1.__proto__ === Person.prototype); //true
console.log(Person.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.__proto__) //null
当我们访问对象的某个属性时,会从对象实例本身开始寻找,没找到就继续搜索指针指向的原型对象,直至找到顶层null为止
我们把上面的例子串起来就是原型链
person1 --> Person.prototype --> Object.prototype --> null
原型对象prototype中都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用
Person.prototype.constructor === Person //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true


原型模式也是有缺点的,但是原型中的所有属性是被很多实例共享,对于包含引用类型值的属性来说,可能会有很多问题
例子:

function Person() { };

Person.prototype = {
constructor: Person,
name: "mnixu",
age: 20,
job: "Student",
friends: ["zhangsan", "lisi"],
sayName: function () {
console.log(this.name)
}
}

var person1 = new Person();
var person2 = new Person();

person1.friends.push("wangwu");
console.log(person1.friends); //["zhangsan", "lisi", "wangwu"]
console.log(person2.friends); //["zhangsan", "lisi", "wangwu"]

console.log(person1.friends === person2.friends) //true


3.组合使用构造函数模式和原型模式

创建自定义类型的常见方式就是组合使用构造函数模式和原型模式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
所以,每个实例都有自己的一份实例属性副本,但同时又共享着对方法的引用。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["zhangsan", "lisi"];
}

Person.prototype = {
constructor: Person,
sayName: function () {
console.log(this.name)
}
}

var person1 = new Person("mnixu", 20, "Student");
var person2 = new Person("lisi", 20, "Student");

person1.friends.push("wangwu")

console.log(person1.friends); //["zhangsan", "lisi", "wangwu"]
console.log(person2.friends); //["zhangsan", "lisi"]
console.log(person1.friends === person2.friends)  //false
console.log(person1.sayName === person2.sayName)  //true








                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐