JavaScript高级程序设计读书分享之8章——8.2创建对象
JavaScript高级程序设计(第4版)读书分享笔记记录
适用于刚入门前端的同志
创建Object的实例
let person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function() { console.log(this.name);
};
对象字面量
let Person = {name:'Tom',age:28,job:'Teacher',sayName(){console.log(this.name)}
}
Person.sayName() // Tom
虽然使用 Object 构造函数或对象字面量可以方便地创建对象,但这些方式也有明显不足:创建具有同样接口的多个对象需要重复编写很多代码。
工厂模式
function createPerson(name,age,job){let o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){console.log(this.name)}return o;
}let person1 = createPerson('Tom',28,'Teacher')
let person2 = createPerson('Jerry',18,'Student')
console.log(person1 ) // {name:'Tom',age:28,job:'Teacher',sayName: ƒ ()}
问题: 这种工厂模式虽 然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)
构造函数模式
function Person(name,age,job){this.name = namethis.age = agethis.job = jobthis.sayName = function(){console.log(this.name)}}let person1 = new Person('Tom',28,'Teacher')
person1.sayName () //Tomlet person2 = new Person('Jerry',18,'Student')
person1.sayName () //Jerry
工厂模式和构造函数模式的区别:
- 没有显式地创建对象。
- 属性和方法直接赋值给了 this。
- 没有 return
要创建 Person 的实例,应使用 new 操作符。以这种方式调用构造函数会执行如下操作:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(使this指向新对象);
- 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象。
问题:
构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍。因此对前面的例子而言,person1 和 person2 都有名为 sayName()的方法,但这两个方 法不是同一个 Function 实例。
解决:
要解决这个问题,可以把函数定义转移到构造函数外部
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName;
}
function sayName() { console.log(this.name);
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
原型模式
function Person(){}
Person.prototype.name = 'Tom'
Person.prototype.age = 28
Person.prototype.job = 'Teacher'
Person.prototype.sayName = function(){console.log(this.name)
}let person1 = new Person()
person1.sayName() // Tomlet person2 = new Person()
person1.sayName() // Tomconsole.log(person1.sayName == person2.sayName); // true
理解原型
无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构 造函数。
前面的例子就可以理解为:
Person.prototype.constructor 指向 Person。
console.log(Person.prototype.constructor === Person); // true
在自定义构造函数时,原型对象默认只会获得 constructor 属性,其他的所有方法都继承自
Object。每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]](__proto__属性)指针就会被赋值为构造函数的原型对象
console.log(Person.prototype.__proto__ === Object.prototype); // true
原型层级
当访问某个实例的属性和方法时,会先在实例上搜索个属性。如果这个属性在实例上存在,所以就不会再搜索原型对象了。而并没有在实例上找到这个属性,所以会继续搜索原型对象并使用定义在原型上的属性。
例子:
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
person1.name = "Greg";
console.log(person1.name); // "Greg",来自实例
console.log(person2.name); // "Nicholas",来自原型
只要给对象实例添加一个属性,这个属性就会遮蔽(shadow)原型对象上的同名属性,也就是虽然 不会修改它,但会屏蔽对它的访问。不过,使用 delete 操作符可以完全删除实例上的这个属性,从而让标识符解析过程能够继续搜索原型对象。
例子:
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
person1.name = "Greg"; console.log(person1.name); // "Greg",来自实例
console.log(person2.name); // "Nicholas",来自原型delete person1.name;
console.log(person1.name); // "Nicholas",来自原型
hasOwnProperty()方法
用于确定某个属性是在实例上还是在原型对象上。这个方法是继承自 Object的,会在属性存在于调用它的对象实例上时返回 true,在原型上返回false。
通过调用 hasOwnProperty()能够清楚地看到访问的是实例属性还是原型属性。
例子:
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};let person1 = new Person();
let person2 = new Person(); console.log(person1.hasOwnProperty("name")); // falseperson1.name = "Greg";
console.log(person1.name); // "Greg",来自实例
console.log(person1.hasOwnProperty("name")); // true
hasPrototypeProperty()
属性首先只存在于原型上,所以 hasPrototypeProperty()返回 true。
实例上也有了这个属性,因此 hasPrototypeProperty()返回 false。
例子:
// 原型对象还是接上面的let person = new Person();
console.log(hasPrototypeProperty(person, "name")); // true person.name = "Greg";
console.log(hasPrototypeProperty(person, "name")); // false
Object.keys()
要获得对象上所有可枚举的实例属性
//原型对象接上面的案例let keys = Object.keys(Person.prototype);
console.log(keys); // "name,age,job,sayName" let p1 = new Person();
p1.name = "Rob";
p1.age = 31;
let p1keys = Object.keys(p1);
console.log(p1keys); // "[name,age]"
Object.getOwnPropertyNames():
如果想列出所有实例属性,无论是否可以枚举,都可以使用
let keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); // "[constructor,name,age,job,sayName]"
Object.getOwnPropertySymbols()
对象迭代
ECMAScript 2017 新增了两个静态方法,Object.values()和 Object.entries()接收一个对象
示例:
const o ={foo: 'bar', baz: 1, qux: {}
}; console.log(Object.values(o)); // ["bar", 1, {}]
console.log(Object.entries((o))); // [["foo", "bar"], ["baz", 1], ["qux", {}]]