js是一门动态语言,js没有类的概念,ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型。
至于继承,js的继承与java这种传统的继承不一样.js是基于原型链的继承.
在javascript里面,每个对象都有一个prototype属性,指向它的原型对象.这个原型对象里面同时还有自己的原型,原型一环扣一环,直到某个对象的原型为null,这一级一级的链结构就是原型结构.
js的原型链继承
继承属性
js属性查找:由于js原型链的存在,当查找一个对象属性时候,不只是在对象上查找,还会沿着该js对象的原型链往上查找,知道找到一个匹配的属性或者查找到原型链末尾.当然如果js对象上与其原型的对象上都有同名的属性,我们遵循该属性的作用域就近原则(术语叫做"属性遮蔽").
var Ele = function(){
this.a = 1;
this.b = 2;
}
var Obj = function(){
this.c = 3;
this.b = 4;
}
Ele.prototype = new Obj();
var ele1 = new Ele();
console.log(ele1.a);//1
console.log(ele1.b);// 2
console.log(ele1.c);// 3
console.log(ele1.d);//undefined
继承方法
在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性/方法。函数的继承与其他的属性继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言(java)的方法重写)。
var student= {
age : 20,
name: 'cp',
sayhi : function(){
console.log('hi');
}
}
var s1 = Object.create(student); //一种创建对象的方法,后面会写博客介绍.s1.prototype是student
s1.name = 'kobe';
console.log( s1.name ); // 'kobe'
console.log( s1.age ); // 20
console.log( s1.sayhi() ); // hi
多种方法创建对象及生产原型链
普通对象字面量创建对象
var o = {
a: 1
};
console.log(o.hasOwnProperty('a')); //true
// o这个对象继承了Object.prototype上面的所有属性
// 所以可以这样使用 o.hasOwnProperty('a').判断一个对象是否还有一个属性
// hasOwnProperty 是Object.prototype的自身属性。
// Object.prototype的原型为null。
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组的实例对象都继承于Array.prototype
// (indexOf, forEach等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于Function.prototype
// (call, bind等方法都是从它继承而来):
// f ---> Function.prototype ---> Object.prototype ---> null
- 使用构造函数(构造器)创建对象
`function Student() {
this.job = '读书';
this.tag = '年轻';
}
function BoyStudent() {
this.sex = 'boy';
}
BoyStudent.prototype = new Student();
var xiaoMing = new BoyStudent();
console.log(xiaoMing.sex); //boy
console.log(xiaoMing.tag); //年轻
// xiaoMing是生成的对象,他的自身属性有'sex'.
// 在BoyStudent被实例化时,BoyStudent.[[Prototype]]指向了Student.prototype.
使用Object.create()创建对象,新对象的原型就是调用 create 方法时传入的第一个参数:
var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
使用 class 关键字
ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不一样的。 JavaScript 仍然是基于原型的,这点一直不变。这些新的关键字包括 class, constructor, static, extends, 和 super.(跟java的关键字一样,构造器,静态,继承,超类)
"use strict";
//类 Person
class Person(){
constructor(name , age){
this.name = name;
this.age = age;
};
}
// 类Girl继承Person
class Girl extends Person(){
constructor(name , age){
super(name , age);
}
getinfo(){
return this.name + ',' + this.age;
}
}
var girl = new Girl('kobe',20);
-
原型继承的性能
在原型链上查找属性比较耗时,对性能有副作用,尽量避免,这在性能要求苛刻的情况下很重要。另外,访问不存在的属性时会遍历整个原型链,浪费资源。
遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。
检测对象的属性是定义在自身上还是在原型链上,有必要使用 hasOwnProperty 方法,所有继承自 Object.proptotype 的对象都包含这个方法,返回布尔值,是 JavaScript 中唯一一个只涉及对象自身属性而不会遍历原型链的方法。
注意:仅仅通过判断值是否为 undefined 还不足以检测一个属性是否存在,一个属性可能存在而其值恰好为 undefined。
关于原生对象的原型扩展
理论上我们不应该去扩展Object.prototype,或者其他内置对象的原型,像Array.prototype等。
我们去扩展内置对象原型的唯一理由是引入新的 JavaScript 引擎的某些新特性,比如 Array.forEach。
理解prototype与Object.getPrototypeOf区别
function A(a){
this.varA = a;
}
// 以上函数 A 的定义中,既然 A.prototype.varA 总是会被 this.varA 遮蔽,
// 那么将 varA 加入到原型(prototype)中的目的是什么?
A.prototype = {
varA : null, // 既然它没有任何作用,干嘛不将 varA 从原型(prototype)去掉?
// 也许作为一种在隐藏类中优化分配空间的考虑?
// https://developers.google.com/speed/articles/optimizing-javascript#Initializing instance variables
// 将会验证如果 varA 在每个实例不被特别初始化会是什么情况。
doSomething : function(){
// ...
}
}
当你 var a1 = new A();js内部就会设置a1.[[prototype]] == A.prototype .
如果你再 var a2 = new A();那么 a1.doSomething 事实上会指向Object.getPrototypeOf(a1).doSomething,
它就是你在 A.prototype.doSomething 中定义的内容。
比如:Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething。
prototype 是用于类型的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
var Fx = function(){};
var fx = new Fx();
相当于
var fx = new Object();
fx[[prototype]] = Fx.prototype;
Fx.call(fx);
在用原型继承编写复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。此外,除非为了兼容新 JavaScript 特性,否则,永远不要扩展原生的对象原型。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。