小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
面向对象编程的三个特征
- 继承:子对象可以继承父对象的属性和行为,亦即父对象拥有的属性和行为,其子对象也就拥有了这些属性和行为
- 封装:事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为
- 多态:允许不同类的对象对同一消息作出响应
Javascript提供了一个构造函数(Constructor)模式
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
比如
class eat{
constructor(food,type){
this.food = food this.type = type
}
}
然后我们就可以根据这个生成实例:
var zao = new eat('豆浆','早餐')
var wu = new eat('米饭','午餐')
zao.food // 豆浆
zap.type // 早餐
这是 zao 和 wu 都有一个 constructor 属性,指向他的构造函数
zao.constructor === eat // true
wu.constructor === eat // true
当然 我们还有另一个方法可以进行判断 instanceof 来进行判断
zao instanceof eat // true
wu instanceof eat// true
构造函数是很好用 但是存在一个内存浪费的问题。
让我们稍微对 eat 类 进行一下修改
class eat{ constructor(food,type){ this.food = food this.type = type } drink(){ alert("吃老鼠") } }
然后每次我们生成实例的时候都会有重复的内容,多占用很多的内存,缺乏效率
能不能让属性和drink()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。
Prototype模式
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
class Cat{
constructor(color,name ){
this.name = name; this.color = color;
}
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){
console.log("吃老鼠")
};
然后,生成实例。
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
cat1.type; // 猫科动物
cat1.eat(); // 吃老鼠
这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
cat1.eat == cat2.eat; //true
当然相对应的我们也有几个方法进行判断:
- hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性
cat1.hasOwnProperty("name") // true
cat1.hasOwnProperty("type") // false
- isPrototypeOf()方法,用来判断某个proptotype对象和某个实例之间的关系
Cat.prototype.isPrototypeOf(cat1) // true
Cat.prototype.isPrototypeOf(cat2) // true
- in运算符,用来判断某个实例是否含有某个属性,不管是不是本地属性
"name" in cat1 // true
"type" in cat1 // true
还可以用来遍历某个对象的所有属性
for(var prop in cat1) {
console.log(`${prop}:${cat1[prop]}`))
}
// 输出
// name:黄色
// color:大毛
// type:猫科动物
// eat:function(){console.log("吃老鼠")}
__proto__用来读取或设置当前对象的prototype对象
无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替
Object.setPrototypeOf():用来设置一个对象的prototype对象下面是一个例子。
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
上面代码将 proto 对象设为 obj 对象的原型,所以从 obj 对象可以读取 proto 对象的属性。 getPrototypeOf 获取:该方法与 setPrototypeOf 方法配套,用于读取一个对象的 prototype 对象。
cat1.__proto__ === Object.getPrototypeOf(p1)