@Dale-Lin
2017-09-16T14:21:10.000000Z
字数 6133
阅读 1159
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的先后无关。
//continueObject.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); //2004var book.year = 2017;console.log(book.edition); //14console.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的实例属性添加。