前面讲完原型链,如今来说继承,加深理解下。数组
什么是对象? 就是一些无序的 key : value 集合, 这个value 能够是 基本值,函数,对象。(注意 key 和 value 之间 是冒号 :
,每一个 key : value 之间 是逗号 ,
)app
var person = { name: 'zdx', age: 18, get: function(){ return '名字:' + this.name + '年纪:' + this.age; } }
这时的 person 就是一个对象函数
读取对象的属性,有两种方式:点 和 [' '],(使用中括号,里面是有引号的)优化
person.name //'zdx' person['age'] //18
以前说过,对象的建立 有三种 方式: 字面量,new,Object.create;ui
可是,这种简单的建立,并不能知足,咱们实际开发的要求。this
好比,每一个人的名字,年纪不同,咱们不可能,每一个人都写一个对象,那不得炸了。prototype
var personA = { name: 'A', age: 28 } var personB = { name: 'B', age: 22 }
因此,这时候,咱们就须要一些建立模式。code
<br/>对象
工厂模式呢,就是咱们写一个方法,而后经过这个方法复制出咱们须要的对象。咱们须要多少个,就复制多少个。(注意这里是用方法(函数) 来生成对象)继承
var createPerson = function(name,age){ var obj = {}, obj.name = name, obj.age = age }
而后咱们就能够这样使用了。
var personA = createPerson('A', 28); var personB = createPerson('B', 22);
可是呢,这种模式,建立的对象,没法归类。也就是说,并不知道 personA,personB 等 是 属于哪一个对象的(哪一个构造函数或者类)。
具备相同特性的一类事物,把它总结成一个 类;好比,人类,有男人,女人,小孩。把他们总结成了人类。
好比:
var obj = {}; obj instanceof Object; //true //instanceof 方法 能够判断 前者是不是 后者的实例对象 var arr = []; arr instanceof Array; //true
因而,构造函数模式来了。
<br/>
要想知道,男人,女人 属于什么类, 人类? 兽类? 这时候就须要 构造函数(类)了。
function Person(name, sex){ //注意,咱们一般把构造函数(类)的首字母大写! this.name = name; this.sex = sex; } var personA = new Person('张三', '男'); var personB = new Person('李四', '女'); console.log( personA instanceof Person ); //true console.log( personB instanceof Person ); //true
这时候 建立出来的对象, 就知道 它属于哪一个构造函数(类)了。
<br/>
这里为啥用了 this 呢?new 一个函数, 为啥会建立出 一个实例对象(咱们把 new 出来的对象 称为 实例对象,简称实例)呢?
这个呢,就要理解new 到底干了啥呢:
<br/>
function New(func){ //func 指传进来的构造函数 var obj = {}; //这里就直接新建一个空对象了,不用new Object了,效果同样的,由于我感受这里讲实现new的功能 再用 new 就不太好。 if(func.prototype != null){ obj.__proto__ = func.prototype; } func.apply(obj,Array.prototype.slice.call(arguments, 1)); //把func构造函数里的this 指向obj对象(你能够理解成func构造函数的this 替换成 obj对象); //Array.prototype.slice.call(arguments, 1);这个就是把New 函数,func 以后传进来的参数,转成数组。 //把该参数数组,传入func构造函数里,并执行func构造函数,好比:属性赋值。 return obj; }
验证下:
function Person(name, sex){ this.name = name; this.sex = sex; this.getName = function(){ return this.name; } } var p = New(Person,"周大侠啊", "男"); console.log( p.getName() ); //"周大侠啊" console.log( p instanceof Person ); //true
去掉new 的写法就是这样:
function Person(name, sex){ var obj = {}; obj.__proto__ = Person.prototype; obj.name = name; obj.sex = sex; obj.getName = function(){ return obj.name; } return obj; } var p = Person("周大侠啊", "男"); console.log( p.getName() ); //"周大侠啊" console.log( p instanceof Person ); //true
<br/>
使用 new 就简化了步骤。
这种模式,也有弊端,你看出来了吗? 有木有发现,每一个实例对象 都有 相同 的 getName 方法,这样,是否是就但占资源了呢,100个实例对象,就新建了 100 个 getName 方法。
原型模式开始!
<br/>
以前的原型链说过,属性、方法的读取是沿着原型链查找的,也就是说,在构造函数的原型对象上 建立的 属性、方法,它的实例对象都可以使用。
function Person(){}; Person.prototype.name = "周大侠啊"; Person.prototype.sex = "男"; Person.prototype.getName = function(){ return Person.prototype.name; } var p = new Person(); console.log( p.getName() ); //"周大侠啊" console.log( p.hasOwnProperty("getName") ); //false getName 不是 p实例的属性
这样,建立的实例对象 都能使用 getName ,而且,也不会给每一个实例对象,添加该属性。
不过,你发现了吗,这样建立的实例对象,都是固定的属性值, 一更改,全部的实例对象获取的值,也都改变了。
那么说了半天,这都是啥呢,别急,正由于上面作的铺垫,才有更好的 方法。我以为,眼尖的同窗,可能已经知道了。
<br/>
构造函数模式 与 原型模式 相结合的模式。
构造函数模式,能够建立 每一个 实例对象 本身的属性, 而原型模式,能够共享,同一个方法。
因此,咱们通常,把对象本身的属性、方法 ,用构造函数模式建立; 公共的方法,用原型模式 建立。
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getName = function(){ return this.name; } var p = new Person("周大侠啊", 22); p.getName();
<br/>
这样就能够完美的建立对象了。
要是你有余力,能够想一想,这里的 this.name
中 this
是怎么回事。
可是,上面这个还能够优化,要是 Person.prototype 上有不少方法, 这种写法就很很差,通常,咱们用这种写法:
function Person(name, age){ this.name = name; this.age = age; } //新建一个对象,给该对象添加 方法, 而后把该对象 赋值给Person.prototype ,该对象就成为 Person (构造函数)的原型对象了。 Person.prototype = { constructor : Person, //给新建的对象,添加constructor 属性,创建与构造函数之间的联系。 getName : function(){ return this.name; }, getAge : function(){ return this.age; } } var p = new Person("周大侠啊", 22); p.getName();
<br/>
这里的 this 又看懂是怎么回事了吗? 那我就简单说一下 this 的知识吧。
关于 this 估计都据说过 谁调用它,this就指向谁,这种说法,不严谨,this 的指向,是 在函数调用(运行)时肯定的!
上面的:
Person.prototype = { constructor : Person, getName : function(){ return this.name; }, } var p = new Person("周大侠啊", 22); p.getName();
this
在getName
函数里,而getName
真正运行 的地方 是 p.getName
,p 是调用者,因此, this 就指向 p(换句话说,这时的this 就是 p)。
你要理解 精髓, 函数调用(运行)时 肯定的调用者 之间的关系。
看这个:
var a = 10; var b = { a : 20, say : function(){ console.log(this.a); } } var c = b.say; c(); //10 非严格模式下,独立调用(无调用者)this 指向全局对象
包含 this
的函数 正在调用(运行) 的时候 是 c();
此时,无调用者,指向全局对象。
<br/>
<br/>
上面作了辣么多铺垫,终于要开始继承的讲解啦!什么是继承呢,其实呢,就是你 想要使用 别人的属性,或者方法; 这时候就要利用继承来实现。
既然是 获得别人的属性,方法,就是继承,那么不就是 除了 Object.prototype 其余都是继承了? dui ! 你说的没错...哈哈
因此,继承的一种就是基于 原型链的了 ,就是属性的查找,读取。
另外一种就是 构造函数继承了。
首先来一个须要被继承的目标
//父类: function Person(name) { this.name = name; } Person.prototype = { constructor: Person, getName : function() { return this.name; } }
<br/>
//子类: function Son(name, age) { Person.call(this, name); //call 方法,把前面函数的this 指向 给定的对象,第二个参数开始,传入 参数,并执行函数。 this.age = age; } //就至关于 function Son(name, age) { //Person(name); 此时 Person 里的 this 等于当前的this this.age = age; } //等同于 function Son(name, age) { this.name = name, this.age = age }
下面检验一下:
var s1 = new Son('周大侠啊', 22); console.log(s1.name); //'周大侠啊' console.log(s1.getName()); //报错,这是Person原型对象上的方法
<br/>
这种是,new 一个父类实例,而后把son.prototype 添加到原型链上去。
//new 一个 Person 实例 赋给 Son.prototype Son.prototype = new Person(); //给子类的原型加方法: Son.prototype.get = function() { return this.name + this.age; }
上面的 Son.prototype = new Person() 实际上就是这样的
//内部就是 var obj = {}; obj.__proto__ = Person.prototype; Son.prototype = obj //把 obj 赋给 Son.prototype 。而obj 这个实例 又指向了Person的原型对象, //这样,Son 就添加到了 Person 的原型链上了。
<br/>
因此:
//给子类的原型加方法: Son.prototype.get = function() { return this.name + this.age; } //就是: obj.get = function(){ //经过给obj添加方法 而后赋值给Son的原型对象 return this.name + this.age; };
验证:
var s2 = new Son('周大侠啊', 22); console.log(s2.get()); //'周大侠啊22' console.log(s2.getName()); //'周大侠啊'
<br/>
function create(proto, options){ //proto表示父类的原型对象,options表示给子类添加的属性 var obj = {}; obj.__proto__ = proto; return Object.defineProperties(obj,options); //Object.defineProperties方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。因此这里就直接return了 } //继承 就这样写了 Son.prototype = create(Person.prototype,{ constructor: { //这种格式的赋值写法, 是 defineProperties 方法规定的写法。 value: Son }, get: { value: function(){ return this.name + this.age; } } }) //验证一下 var s3 = new Son("zdx",22); console.log(s3.getName()); //'zdx' console.log(s3.get()); //'zdx22'
Object.defineProperties具体用法点这里
<br/>
这里是它具体用法
能够直接使用它来完成继承:
Son.prototype = Object.create(Person.prototype,{ constructor: { value: Son }, get: { value: function(){ return this.name + this.age; } } }) //验证一下 var s4 = new Son("zdx",22); console.log(s4.getName()); //zdx console.log(s4.get()); //zdx22