@Dale-Lin
2017-09-16T22:21:10.000000Z
字数 6133
阅读 854
JavaScript
创建对象的方法有两种:
new Object()
(不推荐):
var person = new Object();
person.name = "Nick";
person.age = "20";
var person = {
name:"Nick",
age:"20",
}
注意,对象的属性用 ,
分隔.
另外,在使用对象 字面量 语法时,如果留空其花括号,则与使用 new Object()
构造一样:
var person = {};
var person = new Object();
//两行代码作用一样
function FuncName(prop1, prop2, prop3, func1){
this.prop1 = prop1;
this.func1 = func1;
}
var obj1 = new FuncName(prop7, prop8, prop9, func3);
var obj2 = new FuncName(prop4, prop5, prop6, func2);
注意事项:
1. 函数名 首字母大写 表示该函数为一个构造函数。
2. 创建 obj 实例时,必须使用 new
操作符。(否则函数的属性和方法都将被添加给window对象)
3. 使用 new
调用一个构造函数时,实际发生了:
i. 创建一个新对象。
ii. 使新对象的作用域与构造函数的相同。(使用this指向新对象)
iii. 执行构造函数中的代码。
iv. 返回新对象。(不必使用return)
4. 在构造函数内声明的方法,在每次调用构造函数创建对象实例时,赋予每个新对象的方法都相当于是一个新方法(各不相等)。对于完成相同任务的方法来说是一种资源浪费,可以把函数定义转移到外部作用域来解决这个问题:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
var person1 = new Person("Nicholas", "29", "Software Engineer");
但是将一个在局部作用域作用的函数写在全局作用域下显得不合适。而且这个构造函数失去了封装性。
这些问题可以使用原型模式来解决。
每个函数创建后都有一个 prototype
属性,指向一个对象,这个对象在未声明其他属性时,只有一个 constructor
属性,指向具有该 prototype
属性的函数;可以继续为这个原型对象添加其他属性。
所有通过构造函数创建的对象实例,能够共享构造函数的原型对象的属性和方法(实例的 __proto__
与构造函数的 prototype
相同)。
function Person(){}
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
constructor : Person,
sayName : function(){
alert(this.name);
}
};
理解原型链:
代码读取某对象的属性时,先从对象实例自身开始,如果有,返回该属性值,停止搜索;如果没有,搜索该对象指向的原型对象,如果有该属性,返回它的值。
isPrototypeOf(obj)
来确认原型和对象之间的关系。Object.getPrototypeOf(obj)
来返回一个对象的原型。delete
删除实例中的属性,能使原型中的同名属性被重新读取。hasOwnProperty(prop)
检查某属性是否在对象实例中。 in
操作符:
in
单独使用时,会在通过某对象并能访问到某属性(实例或原型中)时返回 true 。待测属性名应使用字符串形式。
var person1 = {
name: "Nicholas",
}
console.log("name" in person1) //true
for-in
循环用于遍历所有能够通过某对象访问(实例或原型中)的、可枚举(enumerable: true
)的属性。
var o = {
name : "Nicholas",
age : 20,
};
function Person(){
this.job = "Software Engineer";
}
Person.prototype = o;
var person1 = new Person();
//每次循环赋予不同属性,执行相同的代码
for (var prop in person1){
if ( person1.hasOwnProperty(prop) ){
console.log("person." + prop + " = " + person1[prop]);
}
}
//使用 hasOwnProperty() 屏蔽原型中的属性
for-in
循环接受参数时,传入的是被""
包含的以String
形式传入的属性名。Object.getOwnPropertyNames(obj)
Object.keys(obj)
方法,返回一个包含所有可枚举属性的字符串数组。 封装原型对象:
function Person(){}
Person.prototype = {
name : 'Nicholas',
age : 29,
job : 'Software Engineer',
sayName : function () {
console.log(this.name);
}
};
Person.prototype
设置成一个字面量形式声明的新对象,本质上重写了 Person 默认的 prototype 对象,因此它的 constructor 在重写后指向 Object构造函数 ,如果需要该属性的指向,可以:
Person.prototype.constructor = Person;
//或写在封装对象内
但是这样又将 constructor 属性设置成了一个可遍历属性。
可以通过 Object.defineProperty()
来设置 enumerable
属性。
重写原型对象:
使用字面量语法 重写原型对象相当于为构造函数获得了一个新的原型对象,与原来的原型对象切断联系。
在重写前的所有通过该构造函数创建的实例对象,的原型都指向构造函数最初的原型对象(并不指向构造函数)。于是这些实例对象的原型对象并不能获得更新。
使用原型对象的问题:
在某实例对象中修改某属性的 value 时,由于该属性存在于所有实例对象的 __proto__ 中,所以会导致所有有该原型对象的实例对象的属性都被修改。
function Person(){
}
Person.prototype = {
construtor: Person,
name: "Nicholas",
age: 20,
job: "Software Engineer",
friends: ["Shelby", "Count"],
sayName: function(){
console.log(this.name);}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
console.log(person2.friends); //"shelby, Count, Van"
且仅仅使用原型模式,每个对象实例就没有了独自的属性。
组合使用构造函数模式和原型模式(推荐):
使用最广泛的方式是,所有对象共享的属性和方法使用原型模式,而构造函数接受参数来生成每个实例对象的属性。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Sheley", "Count"];
}
Person.prototype = {
constructor: Person,
sayName: function(){console.log(this.name);}
}
稳妥构造函数模式:
不使用this,不使用new调用构造函数。
function Person(name, job, age){
//创建构造函数要返回的对象
var o = new Object();
//定义私有变量和函数
//添加方法
o.sayName = function (){console.log(name);};
//返回对象
return o;
}
保证数据的安全。
.
:
alert(person.name);
['']
来访问对象属性。 Obj['PropName']
alert(person['name']); //使用方括号法时注意,属性名需要引号
使用 ['']
可以通过变量名来访问属性:
var propertyName = 'name';
alert(person[propertyName]);
如果属性名中包含 空格 或 保留字符 ,使用方括号法避免出错:
person['first name'] = 'Joke'; //属性名是可以包含非数字非字母的
不建议使用 ['']
访问对象属性。
configurable
:能否使用delete obj.prop;
删除某属性,能否修改属性的特性,能否把属性修改为访问属性。默认值为true。writable
:能否修改某属性的value
。默认为true。value
:包含某属性的数据值。enumerable
:能够使用 for-in
循环返回某属性。默认为true。除了value
属性可以通过直接声明,configurable
和writable
都需要使用 Object.defineProperties(obj, {props})
来修改。
var person = {};
Object.defineProperties(person,{
"name":{
value: "Nicholas",
configurable: false,
writable: true,
},
"age":{
value: "23",
configurable: true,
writable: false,
}
});
要注意的是,声明了 configurable: false,
的对象属性,即使使用 Object.defineProperties()
也不能再修改除 writable
属性以外的属性了,否则会抛出错误。
在 Object.defineProperties()
内,修改writable
属性并同时声明新的value
的先后无关。
//continue
Object.defineProperties(person,{
"age":{
value: "26",
configurable: true,
writable: true,
}
});
console.log(person.age); //26
configurable
:能否使用delete obj.prop;
删除某属性,能否修改属性的特性,能否把属性修改为访问属性。默认值为true。set
:在写入某属性时调用的函数。默认为 undefined。get
:在读取某属性时调用的函数。默认为 undefined。enumerable
:能够使用 for-in
循环返回某属性。默认为true。这些属性同样需要使用 Object.defineProperties(obj, {props})
来设置。
如果只设置set
或get
,这个对象将 只写 或 只读。
var book = {
_year: 2004,
edition: 1,
};
Object.defineProperties(book, {
"year": {
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > this._year){
this._year = newValue;
this.edition += newValue-2004;
}
}
},
});
console.log(book.year); //2004
var book.year = 2017;
console.log(book.edition); //14
console.log(book.year); //2017
通过原型链的继承,不要忽略 Object 才是最终的原型,再结合原型链搜索规则判断搜索的结果。
将一个构造函数 a的原型 设置成另一个构造函数 b的对象实例 ,即可完成 a对b的继承 :
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//原型替换成SuperType的实例
SubType.prototype = new SuperType();
//相当于SubType = Object.create(new
完成原型对实例的继承后,可以通过对原型(也就是实例)或其原型的访问来修改SuperType,甚至是SuperType.prototype的属性及方法。
但是,一旦修改SuperType.prototype的内容,将会影响所有对象实例的相应内容。
借用构造函数可以使SuperType的代码在SubType创建实例时,在SubType的环境下运行。
function SuperType(name){
this.colors = ["red", "blue", "green"];
this.name = name;
}
function SubType(name){
SuperType.call(this, name);
}
var i1 = new SubType();
i1.colors.push("black");
console.log(i1.colors); //"red","blue","green","black"
var i2 = new SubType("Nicholas");
console.log(i2.colors); //"red","blue","green"
console.log(i2.name); //"Nicholas"
这样就避免了对所有实例对象的无差别影响。
但是每一次创造实例,都要调用一次SuperType构造函数。
在借用继承的基础上,在SuperType.prototype上定义函数,即可避免每次调用时写入共用方法,实现代码的复用。
而且isPrototypeOf()
,instanceof()
方法能够正确识别。
语法
newObj = Object.create(proto[, propertiesObject])
Object.create() 第一个参数是一个对象,作为newObj的__proto__;
第二个参数如同Object.defineProperties()
的第二个参数一样,是一组或几组属性名值对,作为newObj的实例属性添加。