[关闭]
@Dale-Lin 2017-09-16T22:21:10.000000Z 字数 6133 阅读 854

JS Object

JavaScript


创建对象实例

创建对象的方法有两种:

  1. var person = new Object();
  2. person.name = "Nick";
  3. person.age = "20";
  1. var person = {
  2. name:"Nick",
  3. age:"20",
  4. }

注意,对象的属性用 , 分隔.

另外,在使用对象 字面量 语法时,如果留空其花括号,则与使用 new Object() 构造一样:

  1. var person = {};
  2. var person = new Object();
  3. //两行代码作用一样

  1. function FuncName(prop1, prop2, prop3, func1){
  2. this.prop1 = prop1;
  3. this.func1 = func1;
  4. }
  5. var obj1 = new FuncName(prop7, prop8, prop9, func3);
  6. var obj2 = new FuncName(prop4, prop5, prop6, func2);

注意事项:
1. 函数名 首字母大写 表示该函数为一个构造函数。
2. 创建 obj 实例时,必须使用 new 操作符。(否则函数的属性和方法都将被添加给window对象)
3. 使用 new 调用一个构造函数时,实际发生了:
i. 创建一个新对象。
ii. 使新对象的作用域与构造函数的相同。(使用this指向新对象)
iii. 执行构造函数中的代码。
iv. 返回新对象。(不必使用return)
4. 在构造函数内声明的方法,在每次调用构造函数创建对象实例时,赋予每个新对象的方法都相当于是一个新方法(各不相等)。对于完成相同任务的方法来说是一种资源浪费,可以把函数定义转移到外部作用域来解决这个问题:

  1. function Person(name, age, job){
  2. this.name = name;
  3. this.age = age;
  4. this.job = job;
  5. this.sayName = sayName;
  6. }
  7. function sayName(){
  8. alert(this.name);
  9. }
  10. var person1 = new Person("Nicholas", "29", "Software Engineer");

但是将一个在局部作用域作用的函数写在全局作用域下显得不合适。而且这个构造函数失去了封装性。

这些问题可以使用原型模式来解决。


函数的原型

每个函数创建后都有一个 prototype 属性,指向一个对象,这个对象在未声明其他属性时,只有一个 constructor 属性,指向具有该 prototype 属性的函数;可以继续为这个原型对象添加其他属性。
所有通过构造函数创建的对象实例,能够共享构造函数的原型对象的属性和方法(实例的 __proto__ 与构造函数的 prototype 相同)。

  1. function Person(){}
  2. Person.prototype = {
  3. name : "Nicholas",
  4. age : 29,
  5. job : "Software Engineer",
  6. constructor : Person,
  7. sayName : function(){
  8. alert(this.name);
  9. }
  10. };

理解原型链:

代码读取某对象的属性时,先从对象实例自身开始,如果有,返回该属性值,停止搜索;如果没有,搜索该对象指向的原型对象,如果有该属性,返回它的值。

in 操作符:

in 单独使用时,会在通过某对象并能访问到某属性(实例或原型中)时返回 true待测属性名应使用字符串形式

  1. var person1 = {
  2. name: "Nicholas",
  3. }
  4. console.log("name" in person1) //true

for-in 循环用于遍历所有能够通过某对象访问(实例或原型中)的、可枚举(enumerable: true)的属性。

  1. var o = {
  2. name : "Nicholas",
  3. age : 20,
  4. };
  5. function Person(){
  6. this.job = "Software Engineer";
  7. }
  8. Person.prototype = o;
  9. var person1 = new Person();
  10. //每次循环赋予不同属性,执行相同的代码
  11. for (var prop in person1){
  12. if ( person1.hasOwnProperty(prop) ){
  13. console.log("person." + prop + " = " + person1[prop]);
  14. }
  15. }
  16. //使用 hasOwnProperty() 屏蔽原型中的属性

封装原型对象:

  1. function Person(){}
  2. Person.prototype = {
  3. name : 'Nicholas',
  4. age : 29,
  5. job : 'Software Engineer',
  6. sayName : function () {
  7. console.log(this.name);
  8. }
  9. };
  1. Person.prototype.constructor = Person;
  2. //或写在封装对象内

但是这样又将 constructor 属性设置成了一个可遍历属性。
可以通过 Object.defineProperty() 来设置 enumerable 属性。

重写原型对象:

使用字面量语法 重写原型对象相当于为构造函数获得了一个新的原型对象,与原来的原型对象切断联系。
在重写前的所有通过该构造函数创建的实例对象,的原型都指向构造函数最初的原型对象(并不指向构造函数)。于是这些实例对象的原型对象并不能获得更新。

使用原型对象的问题:

在某实例对象中修改某属性的 value 时,由于该属性存在于所有实例对象的 __proto__ 中,所以会导致所有有该原型对象的实例对象的属性都被修改。

  1. function Person(){
  2. }
  3. Person.prototype = {
  4. construtor: Person,
  5. name: "Nicholas",
  6. age: 20,
  7. job: "Software Engineer",
  8. friends: ["Shelby", "Count"],
  9. sayName: function(){
  10. console.log(this.name);}
  11. };
  12. var person1 = new Person();
  13. var person2 = new Person();
  14. person1.friends.push("Van");
  15. console.log(person2.friends); //"shelby, Count, Van"

且仅仅使用原型模式,每个对象实例就没有了独自的属性。

组合使用构造函数模式和原型模式(推荐):

使用最广泛的方式是,所有对象共享的属性和方法使用原型模式,而构造函数接受参数来生成每个实例对象的属性。

  1. function Person(name, age, job){
  2. this.name = name;
  3. this.age = age;
  4. this.job = job;
  5. this.friends = ["Sheley", "Count"];
  6. }
  7. Person.prototype = {
  8. constructor: Person,
  9. sayName: function(){console.log(this.name);}
  10. }


稳妥构造函数模式:
不使用this,不使用new调用构造函数。

  1. function Person(name, job, age){
  2. //创建构造函数要返回的对象
  3. var o = new Object();
  4. //定义私有变量和函数
  5. //添加方法
  6. o.sayName = function (){console.log(name);};
  7. //返回对象
  8. return o;
  9. }

保证数据的安全。


访问对象属性

  1. alert(person.name);
  1. alert(person['name']); //使用方括号法时注意,属性名需要引号

使用 [''] 可以通过变量名来访问属性

  1. var propertyName = 'name';
  2. alert(person[propertyName]);

如果属性名中包含 空格保留字符 ,使用方括号法避免出错:

  1. person['first name'] = 'Joke'; //属性名是可以包含非数字非字母的

不建议使用 [''] 访问对象属性。


数据属性

除了value属性可以通过直接声明,configurablewritable都需要使用 Object.defineProperties(obj, {props}) 来修改。

  1. var person = {};
  2. Object.defineProperties(person,{
  3. "name":{
  4. value: "Nicholas",
  5. configurable: false,
  6. writable: true,
  7. },
  8. "age":{
  9. value: "23",
  10. configurable: true,
  11. writable: false,
  12. }
  13. });

要注意的是,声明了 configurable: false, 的对象属性,即使使用 Object.defineProperties() 也不能再修改除 writable 属性以外的属性了,否则会抛出错误。

Object.defineProperties() 内,修改writable属性并同时声明新的value的先后无关。

  1. //continue
  2. Object.defineProperties(person,{
  3. "age":{
  4. value: "26",
  5. configurable: true,
  6. writable: true,
  7. }
  8. });
  9. console.log(person.age); //26

访问属性

这些属性同样需要使用 Object.defineProperties(obj, {props}) 来设置。
如果只设置setget,这个对象将 只写只读

  1. var book = {
  2. _year: 2004,
  3. edition: 1,
  4. };
  5. Object.defineProperties(book, {
  6. "year": {
  7. get: function(){
  8. return this._year;
  9. },
  10. set: function(newValue){
  11. if(newValue > this._year){
  12. this._year = newValue;
  13. this.edition += newValue-2004;
  14. }
  15. }
  16. },
  17. });
  18. console.log(book.year); //2004
  19. var book.year = 2017;
  20. console.log(book.edition); //14
  21. console.log(book.year); //2017

继承

通过原型链的继承,不要忽略 Object 才是最终的原型,再结合原型链搜索规则判断搜索的结果。

将一个构造函数 a的原型 设置成另一个构造函数 b的对象实例 ,即可完成 a对b的继承

  1. function SuperType(){
  2. this.property = true;
  3. }
  4. SuperType.prototype.getSuperValue = function(){
  5. return this.property;
  6. };
  7. function SubType(){
  8. this.subproperty = false;
  9. }
  10. //原型替换成SuperType的实例
  11. SubType.prototype = new SuperType();
  12. //相当于SubType = Object.create(new

完成原型对实例的继承后,可以通过对原型(也就是实例)或其原型的访问来修改SuperType,甚至是SuperType.prototype的属性及方法。
但是,一旦修改SuperType.prototype的内容,将会影响所有对象实例的相应内容。

借用继承

借用构造函数可以使SuperType的代码在SubType创建实例时,在SubType的环境下运行。

  1. function SuperType(name){
  2. this.colors = ["red", "blue", "green"];
  3. this.name = name;
  4. }
  5. function SubType(name){
  6. SuperType.call(this, name);
  7. }
  8. var i1 = new SubType();
  9. i1.colors.push("black");
  10. console.log(i1.colors); //"red","blue","green","black"
  11. var i2 = new SubType("Nicholas");
  12. console.log(i2.colors); //"red","blue","green"
  13. console.log(i2.name); //"Nicholas"

这样就避免了对所有实例对象的无差别影响。
但是每一次创造实例,都要调用一次SuperType构造函数。

组合继承

在借用继承的基础上,在SuperType.prototype上定义函数,即可避免每次调用时写入共用方法,实现代码的复用。
而且isPrototypeOf()instanceof()方法能够正确识别。

原型式继承

语法
newObj = Object.create(proto[, propertiesObject])
Object.create() 第一个参数是一个对象,作为newObj的__proto__;
第二个参数如同Object.defineProperties()的第二个参数一样,是一组或几组属性名值对,作为newObj的实例属性添加。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注