javascript红宝书关于对象、类与面向对象编程的重点230~290的重点知识
对象的声明方式
//第一种方法
var person = new Object();
person.name = "Gaofeng";
person.age = 23;
person.job = "web fronter";
person.sayName = function () {
console.log(this.name);
};
//第二种方法
let person = {
name: "gaofeng",
age: 23,
sayName() {
console.log(this.name);
},
};
//第三种申明属性的方法
let person1 = {};
Object.defineProperty(person1, "name", {
configurable: true, //表示属性是否可以通过 delete 删除并重新定义
writable: true, //表示属性的值是否可以被修改
enumerable: true, //标示属性是否可以枚举列出
value: "Nicholas", //包含属性实际的值。
});
console.log(person1.name);
person1.name = "nelsen";
console.log(person1.name);
工厂模式
抽象创建特定对象的方法,这种工厂模式虽然可以解决创建多个类似对象的问题,但没有解决对象标识问题(即新创建的对象是什么类型)。
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 p1 = createPerson("zhangsan", 23, "tester");
p1.sayName();
let p2 = createPerson("lisi", 24, "fronter");
p2.sayName();
//zhangsan
//lisi
构造函数模式
特点:
函数名大写了
没有显示创建一个对象
属性和方法直接赋值给了this
没有返回值return
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
};
}
let p1 = new Person("zh", 23, "tester");
let p2 = new Person("lisi", 25, "fronter");
p1.sayName();
p2.sayName();
/* zh
* lisi
*/
//这两个对象都有一个constructor 属性指向 Person
console.log(p1.constructor == Person); // true
console.log(p2.constructor == Person); // true
new一个构造函数的时候,主要执行了一下几步
- 隐式的创建了一个新对象
- 新对象的prototype属性指向了构造函数的prototype属性
- 构造函数内部的this被赋值为新对象的this
- 执行构造函数内部的代码,即为新对象添加属性和方法
- 如果构造函数返回非空对象,则返回这个对象,否则返回新创建的对象
构造函数不一样要写成函数声明的方式,函数表达式也是可以的
let Person = function (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
};
}
构造函数的问题,是构造函数内部定义的方法啊,会在每个实例上创建一遍,这是一种浪费!前面的p1和p2来说,sayName方法是两个不同的方法。
因为做的是同一件事情,所以没必要创建两个不同的方法
let Person = function (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName
}
function sayName(){
console.log(this.name);
}
let p1 = new Person("zh", 23, "tester");
let p2 = new Person("lisi", 25, "fronter");
p1.sayName();
p2.sayName();
此时的p1.sayName和p2.sayName就是引用了全局的sayName,
但是其封装性也就没有了,一个方法还好,但是实际业务中会有很多方法,这样看起来就会很散乱!
原型模式
每个函数创建的时候,都会默认的带上一个prototype属性,包含了实例的属性方法。好处是在prototype上面创建的属性和方法会被所以实例共享,原来在构造函数内部赋值的属性和方法现在可以赋值在原型prototype上面
function Animal() {}
Animal.prototype.name = "duck";
Animal.prototype.eat = function () {
return "fish";
};
```js
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
};
}
let p1 = new Person("zh", 23, "tester");
let p2 = new Person("lisi", 25, "fronter");
p1.sayName();
p2.sayName();
/* zh
lisi */
function Animal() {}
Animal.prototype.name = "duck";
Animal.prototype.eat = function () {
return "fish";
};
let a1 = new Animal();
console.log(a1.name);
console.log(a1.eat());
let a2 = new Animal();
console.log(a2.name);
console.log(a2.eat());
/*
duck
fish
duck
fish
*/
可以看到创建的两个实例属性,返回的是一模一样的结果!
在对象中实现get/set定义对象的属性
//get/set访问器属性
let obj = {
name: "gaofeng",
_age: 23 // _私有属性的标志
}
Object.defineProperty(obj, 'age', {
get() {
return this._age
},
set(newValue) {
this._age = newValue;
this.name += " " + this._age - 3
}
})
obj.age = 30
console.log(obj.age);
console.log(obj.name);
实例与构造函数之间的关系,如下:
function Person(name) {
this.name = name
}
let p1 = new Person()
//chrome浏览器中
console.log(Person.prototype, 'Person');
/*
{
constructor: ƒ Person(name),
[[Prototype]]: Object
}
*/
console.log(p1.__proto__, 'p1')
/*
{
constructor: ƒ Person(name),
[[Prototype]]: Object
}
*/
console.log(Person.prototype.constructor == Person)
console.log(Person.prototype.__proto__.constructor == Object)
console.log(Person.prototype.__proto__.__proto__ == null)
console.log(Person.prototype.__proto__, 'Person.prototype.__proto__')
/*
{
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf()
}
*/
//实例与构造函数没有关系,实例与构造函数原型对象有关系
console.log(p1.__proto__ == Person.prototype, '__proto__')
console.log(p1.__proto__.constructor === Person, 'constructor')
/*
true "__proto__"
demo2.html:69 true "constructor"
*/
下图很清晰额展示了这种关系
通过对象访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身。如果在这个 实例上发现了给定的名称,则返回该名称对应的值。如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值。