# 对象的特性
- 封装
- 继承
# 封装
封装: 将对象的所有组成部分组合起来,尽可能的隐藏对象的部分细节,使其受到保护,只提供有限的接口与外部发生联系。
例如同时生成多个相同的对象,可以将生成对象的过程打包直接调用,这个过程就是封装
优点:
- 安全,使用时无法看到具体实现细节,只需要直接调用
- 便于修改操作
# 封装方法
工厂函数(不推荐使用)
将创建对象并赋值的过程封装成一个函数
function person(name,sex){ var person = {}; person.name = name; person.sex = sex; person.say = function(){ alert("说话"); } return person; } var zhangsan = person("张三","man"); alert(zhangsan.name);
构造函数(每创建一个对象,会把相同的代码存储到内存中,会造成对内存的浪费)
function person(name,sex){ this.name = name; this.sex = sex; this.say = function(){ alert("说话"); } } var lisi = new person("李四","boy"); alert(lisi.sex);
每次使用工厂函数或构造函数,内部的属性和方法都会在内存中重新开辟一个控件存储生成的对象,导致某些相同的方法或属性被重复保存,占用内存。
prototype方法(会把共享的方法或属性放到代码段中来存储,它不能共享对象)
实例一:person.prototype.eat=function(){ alert("吃饭"); } var lisi=new person("李四","boy"); lisi.eat();
实例二:
person.prototype.aaa = {name:"王五"}; var lisi = new person("李四","boy"); var zhaoliu = new person("赵六","boy"); alert(lisi.aaa.name = "xiaosi"); //xiaosi alert(zhaoliu.aaa.name); //xiaosi 将原型上的属性值一起改了
混合函数 最佳的一种方法,构造函数与prototype的结合,根据实际情况考虑
私有属性方法放到构造函数中,共有方法放到原型中
function person(user, sex){ this.user = user this.sex = sex } person.prototype = { coding: funciton(){ alert("写代码") } }
对象在内存中存储方式:
# 对象的继承
继承:一个对象拥有另一个对象的属性与方法
- 父类(基类):被继承的对象,
- 子类:继承的对象。
优点:
提高代码的重用性,提高代码的逻辑性与可维护性。
# 继承方式
# 原型继承(将父类的实例作为子类的原型)
原理:对象访问属性方法会遵循 "构造函数 -> 原型"的顺序,所以将父类的实例放到子类原型时, 子类实例化出的对象就可以访问到原型上父类的内容,从而实现了继承。
function Animal(){
eat: function(food){
alert(food)
}
}
function Cat(){ }
Cat.prototype = new Animal(); // 将父类(Animal)的实例作为子类(Cat)的原型
var tom = new Cat();
cat.eat('fish') // 弹出 'fish'
console.log(tom instanceof Animal); //true
console.log(tom instanceof Cat); //true
# 对象冒充继承
- call
格式:fun.call(obj2,参数1, 参数2...)
本质上来说,call方法实际上就是要改变fun函数内的this指向。
function Animal () {
this.eat = function(food){
alert(food)
}
}
Animal.prototype.say = function(sound){
alert(sound)
}
function Cat () {
this.name = "tom";
Animal.call(this)
}
var cat = new Cat ();
cat.eat('鱼'); //OK
cat.say("喵喵"); //Error 对象冒充只能继承构造函数上的属性与方法
对象冒充继承方式只能继承构造函数上的属性与方法,不能继承原型上属性与方法。
2. apply
用法基本与call相同,函数的参数通过数组传递
格式:fun.apply(obj2,[参数1, 参数2...])
function Animal () {
this.eat = function(food){
alert(food)
}
}
Animal.prototype.say = function(sound){
alert(sound)
}
function Cat () {
this.name = "tom";
Animal.apply(this)
}
var cat = new Cat ();
cat.eat('鱼'); //OK
cat.say("喵喵"); //Error 对象冒充只能继承构造函数上的属性与方法
# ES6类继承
通过
extends
关键字实现类与类之间的继承,然后实例化子类,来实现继承。详见下一节
# 原型链
# 原型链
继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。 前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签 名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。
通过原型链访问对象属性与方法顺序为:构造函数-->构造函数原型-->原型链
原型链:
当访问对象的属性或方法时,该属性或方法会在对象本身调用,对象本身没有则去对象本身的构造函数调用,本身构造函数没有则去父类的构造函数调用、父类的原型...以此类推,直到寻找至Object、以及Object的原型、null。最后属性不存在时会得到undefined
,方法不存在则会报错。
ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用 类型的属性和方法。重温一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有 一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味 着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函 数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想。
Object.prototype.say=function(){
alert("Object的方法");
}
function person(){
this.say=function(){
alert("person的方法");
}
}
person.prototype.say=function(){
alert("person原型的方法");
}
function student(){
this.say=function(){
alert("student的方法");
}
}
student.prototype=new person();
var xiaoming=new student();
xiaoming.say=function(){
alert("xiaoMing的方法");
}
xiaoming.say();