小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
1、生成实例对象的原始模式
var Cat = {
name : '',
color : ''
}
var cat1 = {}
cat1.name = "大毛"
cat1.color = "黄色"
var cat2 = {}
cat2.name = "二毛"
cat2.color = "黑色"
优点:将两个属性封装在一个对象里,这是最简单的封装
问题:
-
多生成几个实例,代码就一直在重复冗余;
-
实例与原型之间,没有半点关联
2、原始模式的改进
function Cat(name,color) {
return {
name:name,
color:color
}
}
var cat1 = Cat("大毛","黄色")
var cat2 = Cat("二毛","黑色")
3、构造函数模式
构造函数实际上也就是个普通函数,只是里面采用this,this变量会绑定到实例对象上
function Cat(name,color) {
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黄色")
var cat2 = new Cat("二毛","黑色");
alert(cat1.name); // 大毛
alert(cat1.color); // 黄色
这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数
通过instanceof运算符,可以验证原型对象与实例对象的关系
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true
构造函数模式的问题:
像是一些不变的属性或方法,如果都在构造函数里定义,就会出现每次生成实例,都为重复的内容,多占用了内存
4. 采用Prototype模式修订构造函数模式的问题
将不变的属性跟函数直接定义在prototype上
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){alert("吃老鼠")};
然后,生成实例
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠
这时所有实例的type属性和eat()方法,都是同一个内在地址,指向prototype对象
alert(cat1.eat == cat2.eat); //true
Cat.prototype.constructor == Animal
cat1.constructor == Cat.prototype.constructor
Cat.prototype = new Animal()
cat1.constructor == Animal
自己实现个 new 操作符
function create(Con,...args){
var obj = {}
// 将 obj 实例与 Con.prototype 关联起来,达到可以访问构造函数 Con 对应的原型链上的属性和方法
Object.setPrototypeOf(obj,Con.prototype) //obj.__proto__ = Con.prototype
// 执行 Con 构造函数中的代码,将 this 绑定为当前实例对象 obj ,并传入剩余的参数
var result = Con.apply(obj,args) // var result = Con.call(obj,...args)
// 如果返回的是引用类型的对象,则返回;否则默认返回当前实例对象obj
return result instanceof Object ? result : obj
}
验证:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.speak = function() {
alert(this.name + "speak!")
}
var p = create(Person,"张三",3)
p.speak() //张三speak!