创建对象的方式
最基本的方式
var box = new Object(); //创建一个 Object 对象
box.name = 'Lee'; //创建一个 name 属性并赋值
box.age = 100; //创建一个 age 属性并赋值
box.run = function() { //创建一个 run()方法并返回值
return this.name + this.age + '运行中...';
};
alert(box.run()); //输出属性和方法的值
缺点:想创建一个类似的对象,就 会产生大量的代码。
var box2 = new Object();
box2.name = 'Jack';
box2.age = 200;
box2.run = function() {
return this.name + this.age + '运行中...';
};
alert(box2.run());
工厂方式 为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法 就是为了解决实例化对象产生大量重复的问题。
function createObject(name, age) { //集中实例化的函数
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function() {
return this.name + this.age + '运行中...';
};
return obj;
}
var box1 = createObject('Lee', 100); //第一个实例
var box2 = createObject('Jack', 200); //第二个实例
alert(box1.run());
alert(box2.run()); //保持独立
alert(typeof box1); //Object
alert(box1 instanceof Object); //true
缺点: 工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法 搞清楚他们到底是哪个对象的实例
构造函数方式
即解决了重复实例化的问题,又解决了对象识别的问题
使用了构造函数的方法,和使用工厂模式的方法他们不同之处如下:
1.构造函数方法没有显示的创建对象(new Object());
2.直接将属性和方法赋值给 this 对象;
3.没有 renturn 语句。
function Box(name, age) { //构造函数模式
this.name = name;
this.age = age;
this.run = function() {
return this.name + this.age + '运行中...';
};
}
var box1 = new Box('Lee', 100); //new Box()即可
var box2 = new Box('Jack', 200);
alert(box1.run());
alert(box1 instanceof Box); //很清晰的识别他从属于 Box
关于 this 的使用,this 其实就是代表当前作用域对象的引用。如果在全局范围 this 就代 表 window 对象,如果在构造函数体内,就代表当前的构造函数所声明的对象。
原型
通过prototype方式添加原型
- 我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是 包含可以由特定类型的所有实例共享的属性和方法。
function Box() {} //声明一个构造函数
Box.prototype.name = 'Lee'; //在原型里添加属性
Box.prototype.age = 100;
Box.prototype.run = function() { //在原型里添加方法
return this.name + this.age + '运行中...';
};
var box1 = new Box();
var box2 = new Box();
// alert(box1.run == box2.run); //true,方法的引用地址保持一致
原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。proto 属性是实例指向原型对象的一个指针
console.log(box1.__proto__)
它的作用就是指向构造函数的原型属性 constructor。 通过这两个属性,就可以访问到原型里的属性和方法了。
console.log(box1.__proto__.constructor)
console.log(Box.prototype.constructor)
判断一个对象是否指向了该构造函数的原型对象,
alert(Box.prototype.isPrototypeOf(box1));
如何判断属性是在构造函数的实例里, 还是在原型里? 可以使用 hasOwnProperty() 函数
alert(box.hasOwnProperty('name')); //实例里有返回 true,否则返回 false
原型模式的执行流程:
1.先查找构造函数实例里的属性或方法,如果有,立刻返回;
2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;
字面量方式创建原型
问题: 字面量创建的方式使用 constructor 属性不会指向实例,而会指向 Object
答: 字面量方式为什么 constructor 会指向 Object?因为 Box.prototype={};这种写法其实 就是创建了一个新对象。而每创建一个函数,就会同时创建它 prototype,这个对象也会自 动获取 constructor 属性。所以,新对象的 constructor 重写了 Box 原来的 constructor,因此会 指向新对象,那个新对象没有指定构造函数,那么就默认为 Object。
Box() {};
Box.prototype = { //使用字面量的方式
name: 'Lee',
age: 100,
run: function() {
return this.name + this.age + '运行中...';
}
};
解决方式:原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型。
Box.prototype = { constructor : Box, //直接强制指向即可 };
原型模式创建对象的缺点:
原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺 点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优点,那就是共享。 原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性 也还可以。但如果属性包含引用类型,就存在一定的问题:
function Box() {};
Box.prototype = {
constructor: Box,
name: 'Lee',
age: 100,
family: ['父亲', '母亲', '妹妹'], //添加了一个数组属性
run: function() {
return this.name + this.age + this.family;
}
};
var box1 = new Box();
box1.family.push('哥哥'); //在实例中添加'哥哥' alert(box1.run());
var box2 = new Box();
alert(box2.run()); //共享带来的麻烦,也有'哥哥'了
解决方式:
- 以组合构造函数+原型模式 (解决了传参和引用共享的问题)
function Box(name, age) { //不共享的使用构造函数
this.name = name;
this.age = age;
this.family = ['父亲', '母亲', '妹妹'];
};
Box.prototype = { //共享的使用原型模式
constructor: Box,
run: function() {
return this.name + this.age + this.family;
}
};
继承
ECMAScript 只支持继承,不支持接口实现,而实现 继承的方式依靠原型链完成。 (1)原型继承 缺点:比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
function Box() { //Box 构造
this.name = 'Lee';
}
function Desk() { //Desk 构造
this.age = 100;
}
Desk.prototype = new Box(); //Desc 继承了 Box,通过原型,形成链条
var desk = new Desk();
alert(desk.age);
alert(desk.name); //得到被继承的属性
2.对象冒充继承的方式(为了解决引用共享和超类型无法传参的问题) 缺点: 借用构造函数虽然解决了刚才两种问题,但没有原型,复用则无从谈起。
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
function Desk(age) {
Box.call(this, age); //对象冒充,给超类型传参
}
var desk = new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push('AAA'); //添加的新数据,只给 desk
alert(desk.name);
3.组合继承(原型链+借用构造函数)
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
Box.prototype.run = function() {
return this.name + this.age;
};
function Desk(age) {
Box.call(this, age); //对象冒充
}
Desk.prototype = new Box(); //原型链继承
var desk = new Desk(100);
alert(desk.run());
4.原型式继承 都es6还用这个干啥
5.寄生式继承 都es6 class了面试还问,脑子有坑