繼承的實(shí)現(xiàn)方式
在前面缰贝,我們的結(jié)論就是可以通過繼承來讓一個(gè)對象可以使用另一個(gè)對象的屬性和方法,那怎么實(shí)現(xiàn)繼承呢蠢古?
- 最簡單的繼承實(shí)現(xiàn)
混入式繼承
<script type="text/javascript">
var obj = {
name:"布萊德皮特",
age:12,
sayHello: function () {
console.log("Hello World");
}
}
var o = {};
//混入式繼承
for(var k in obj){
o[k] = obj[k];
}
console.log(o);
</script>
- 原型繼承
<script type="text/javascript">
//原型繼承
//利用原型中的成員可以被其他相關(guān)的對象共享這一特性玩焰,可以實(shí)現(xiàn)繼承
//這種實(shí)現(xiàn)繼承的方式,就叫做原型繼承
// 1募疮,給原型對象中添加成員(通過對象的動態(tài)特性) 不是嚴(yán)格意義上的繼承
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log("Hi,nihao");
}
var p = new Person("迪麗熱巴",24);
p.sayHello();
//此方式屬于對象繼承了原型對象
</script>
- 直接替換原型對象的方式繼承
<script type="text/javascript">
// 2炫惩,直接替換原型對象
function Son(name, age){
this.name = name;
this.age = age;
}
var parent = {
sayHello: function () {
console.log("Hi,Wife");
}
}
Son.prototype = parent;
var s = new Son("蠟筆小新",12);//一定要在設(shè)置原型后面
console.log(Son.prototype);
s.sayHello();
//s對象繼承了原型對象(parent對象)
</script>
注意:使用體會原型的方式實(shí)現(xiàn)繼承的時(shí)候,原有的原型中的成員會消失
- 利用混入的方式給原型對象添加成員
<script type="text/javascript">
// 3阿浓,利用混入的方式給原型對象添加成員
function Son(name, age){
this.name = name;
this.age = age;
}
var s = new Son("蠟筆小新",12);//可以在設(shè)置原型之前
var parent = {
sayWife: function () {
console.log("Hi,My Wife!");
}
}
for(var k in parent){
Son.prototype[k] = parent[k];
}
s.sayWife();
</script>
- 經(jīng)典繼承方式的實(shí)現(xiàn)
<script type="text/javascript">
//《JavaScript語言精粹》作者提出了一個(gè)方式來實(shí)現(xiàn)繼承
function inherit(obj){
var o = {};
o.__proto__ = obj;
return o;
}
var o = inherit({name:"張三"});
console.log(o);
//經(jīng)典繼承的語法
//Object.create(obj)
//返回值為一個(gè)對象他嚷,繼承自參數(shù)中的obj
//這個(gè)方法是ES5中出來的,所以存在兼容性問題
// var o = {
// name:"李四"
// }
// var obj = Object.create(o);
// console.log(obj);
//如何處理Object.create的兼容性問題
var o = {
name:"李四"
}
//檢測瀏覽器的能力芭毙,如果沒有Object.create方法就給他添加一個(gè)(不推薦使用)
if(Object.create){
var obj= Object.create(o)
}else{
Object.create = function (o) {
function F(){}
F.prototype = o;
var obj = new F;
return obj;
}
var obj = Object.create()
}
var o1 = Object.create(o);
console.log(o1);
//自己定義個(gè)函數(shù)
function create(obj){
if(Object.create){
return Object.create(obj)
}else{
Object.create = function (obj) {
function F(){}
F.prototype = obj;
return new F;;
}
}
}
var o = {
age:12,
}
var obj = create(o);
console.log(obj);
</script>
原型繼承
每一個(gè)構(gòu)造函數(shù)都有prototype原型屬性筋蓖,通過構(gòu)造函數(shù)創(chuàng)建出來的對象都繼承自該原型屬性。所以可以通過更改構(gòu)造函數(shù)的原型屬性來實(shí)現(xiàn)繼承退敦。
function Dog(){
this.type = "yellow Dog";
}
function extend(obj1, obj2){
for (var k in obj2){
obj1[k] = obj2[k];
}
};
//使用混入的方式粘咖,將屬性和方法添加到構(gòu)造函數(shù)的原型屬性上,構(gòu)造函數(shù)所創(chuàng)建出來的實(shí)例就都有了這些屬性和方法侈百。
extend(Dog.prototype, {
name:"",
age:"",
sex:"",
bark:function(){}
})
//使用面向?qū)ο蟮乃枷氚裡xtend方法重新封裝
//extend是擴(kuò)展的意思瓮下,誰要擴(kuò)展就主動調(diào)用extend這個(gè)方法
//所以extend應(yīng)該是對象的方法,那現(xiàn)在我們要擴(kuò)展的是構(gòu)造函數(shù)的原型對象
//所以給構(gòu)造函數(shù)的原型對象添加一個(gè)extend方法
//如下:
Dog.prototype.extend = function(obj){
for (var k in obj){
this[k]=obj[k];
}
}
//調(diào)用方式就變成了下面這種形式
Dog.prototype.extend({
name:"",
age:"",
sex:"",
bark:function(){}
});
繼承的應(yīng)用
<script type="text/javascript">
var arr = [1,2,5,3,8,1];
//給數(shù)組添加一個(gè)sayHello方法
Array.prototype.sayHello = function () {
console.log("您好,我告訴你數(shù)組的長度是:"+this.length);
}
arr.sayHello();
//但是這樣屬于擴(kuò)展內(nèi)置對象钝域,就是給內(nèi)置對象新增成員
//在多人開發(fā)時(shí)讽坏,大家都擴(kuò)展內(nèi)置對象,如果名稱一樣會有問題
//內(nèi)置對象中原來有的成員例证,可能會被替換路呜,那內(nèi)置對象的功能會喪失
//在ECMScript升級時(shí),擴(kuò)展內(nèi)置對象的新增成員可能有被覆蓋的危險(xiǎn)
//所以在項(xiàng)目中應(yīng)避免擴(kuò)展內(nèi)置對象
//避免擴(kuò)展內(nèi)置對象战虏,使用繼承的方式
function MArray(){
}
var arr = new Array();
MArray.prototype = arr;
//mArr這個(gè)對象就繼承自arr
var mArr = new MArray();
mArr.push(1);
mArr.push(2,3,4,5);
console.log(mArr);//打蛹鹪住:(5) [1, 2, 3, 4, 5]
</script>
屬性搜索原則
訪問一個(gè)對象的成員的時(shí)候,首先是在實(shí)例中找烦感,沒有找到, 就去原型中找, 但是原型中沒有怎么辦?
屬性搜索原則
所謂的屬性搜索原則巡社,也就是屬性的查找順序,在訪問對象的成員的時(shí)候手趣,會遵循如下的原則:
- 首先在當(dāng)前對象中查找晌该,如果找到肥荔,停止查找,直接使用朝群,如果沒有找到燕耿,繼續(xù)下一步
- 在該對象的原型中查找,如果找到姜胖,停止查找誉帅,直接使用,如果沒有找到右莱,繼續(xù)下一步
- 在該對象的原型的原型中查找蚜锨,如果找到,停止查找慢蜓,直接使用亚再,如果沒有找到,繼續(xù)下一步晨抡。
- 繼續(xù)往上查找氛悬,直到查找到
Object.prototype
還沒有, 那么是屬性就返回undefied
,是方法耘柱,就報(bào)錯(cuò)xxx is not a function
如捅。
原型鏈
每一個(gè)對象都有原型屬性,那么對象的原型屬性也會有原型屬性调煎,所以這樣就形成了一個(gè)鏈?zhǔn)浇Y(jié)構(gòu)伪朽,我們稱之為原型鏈。
原型鏈結(jié)構(gòu)
凡是對象就有原型, 原型又是對象, 因此凡是給定義一個(gè)對象, 那么就可以找到他的原型, 原型還有原型. 那么如此下去, 就構(gòu)成一個(gè)對象的序列. 稱該結(jié)構(gòu)為原型鏈.
使用構(gòu)造函數(shù)創(chuàng)建出對象, 并且沒有利用賦值的方式修改原型, 就說該對象保留默認(rèn)的原型鏈.
默認(rèn)原型鏈結(jié)構(gòu)是什么樣子呢?
function Person() {
}
var p = new Person();
// p 具有默認(rèn)的原型鏈
默認(rèn)的原型鏈結(jié)構(gòu)就是:當(dāng)前對象 -> 構(gòu)造函數(shù).prototype -> Object.prototype -> null
在實(shí)現(xiàn)繼承的時(shí)候, 有時(shí)會利用替換原型鏈結(jié)構(gòu)的方式實(shí)現(xiàn)原型繼承, 那么原型鏈結(jié)構(gòu)就會發(fā)生改變
<script type="text/javascript">
//1汛蝙,什么是原型鏈
// 每個(gè)構(gòu)造函數(shù)都有原型對象
// 每個(gè)對象都有構(gòu)造函數(shù)
// 每個(gè)構(gòu)造函數(shù)的原型對象都是一個(gè)對象
// 那么這個(gè)原型對象也會有構(gòu)造函數(shù)
// 那么這個(gè)原型對象的構(gòu)造函數(shù)也會有原型對象
// 這樣就會形成一個(gè)鏈?zhǔn)降慕Y(jié)構(gòu),稱為原型鏈
function Person(){
}
var p = new Person();
//p的原型是一個(gè)Object對象
console.log(p.__proto__);
// p---->Person.prototype ----> Object.prototype ----> null
//屬性的搜索規(guī)則
// 1.當(dāng)訪問一個(gè)對象的成員的時(shí)候朴肺,會現(xiàn)在自身找有沒有,如果找到直接使用窖剑,
// 2.如果沒有找到,則去當(dāng)前對象的原型對象中去查找戈稿,如果找到了直接使用西土,
// 3.如果沒有找到,繼續(xù)找原型對象的原型對象鞍盗,如果找到了需了,直接使用
// 4.如果沒有找到,則繼續(xù)向上查找般甲,直到Object.prototype肋乍,如果還是沒有,就報(bào)錯(cuò)
//原型繼承
//通過修改原型鏈結(jié)構(gòu)實(shí)現(xiàn)的繼承敷存,就叫做原型繼承
</script>
復(fù)雜的原型鏈
<script type="text/javascript">
function Animal(){
this.age = 8;
}
Human.prototype = new Animal;
Human.prototype.constructor = Human;
function Human(){
this.name = "楊迪";
}
Teacher.prototype = new Human;
Teacher.prototype.constructor = Teacher;
function Teacher(){
this.work = "教書";
}
BadTeacher.prototype = new Teacher;
BadTeacher.prototype.constructor = BadTeacher;
function BadTeacher(){
this.teaching = "打人";
}
var bt = new BadTeacher;
// console.log(bt);
//Teacher {work: "教書", constructor: function}
console.log(bt.__proto__);
//Human {name: "楊迪", constructor: function}
console.log(bt.__proto__.__proto__);
//Animal {age: 8, constructor: function}
console.log(bt.__proto__.__proto__.__proto__);
//Object {constructor: function}
console.log(bt.__proto__.__proto__.__proto__.__proto__);
//Object {__defineGetter__: function, __defineSetter__: function, hasOwnProperty: function, __lookupGetter__: function, __lookupSetter__: function…}
console.log(bt.__proto__.__proto__.__proto__.__proto__.__proto__);
//null
console.log(bt.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__);
var arr = [];
console.log(arr);
//[constructor: function, toString: function, toLocaleString: function, join: function, pop: function…]
console.log(arr.__proto__);
</script>
Object.prototype的成員
- constructor
- hasOwnProperty
- propertyIsEnumerable
- Object.defineProperty();
- toString 和 toLocaleString
- valueOf
- __proto__
<script type="text/javascript">
//1.constructor
//原型對象內(nèi)的一個(gè)屬性墓造,指向該原型對象相關(guān)聯(lián)的構(gòu)造函數(shù)
//2.hasOwnProperty
//一個(gè)方法,用來判斷對象本身(不包含原型)是否擁有某個(gè)屬性
function People(){
this.name = "王久";
}
People.prototype.name = "張三";
var pp = new People();
console.log(pp.name);
console.log(pp.hasOwnProperty("name"));//true
console.log(pp.hasOwnProperty("__proto__"));//false
//3.propertyIsEnumerable
// 1. 判斷屬性是否屬于對象本身
// 2. 判斷屬性是否可以被遍歷
console.log(pp.propertyIsEnumerable("name"));//true
// Object.defineProperty();
// 使用以上方法添加屬性的時(shí)候,可以附加一些信息觅闽,
// 例如這個(gè)屬性是否可寫 可讀 可遍歷
//4.toString 和 toLocaleString
var o = {};
console.log(o.toString());//[object Object]
console.log(o.toLocaleString());//[object Object]
var now = new Date();
console.log(now.toString());//Wed Jul 19 2017 23:57:53 GMT+0800 (China Standard Time)
console.log(now.toLocaleString());//2017/7/19 下午11:57:53
function Person(){}
var p = new Person();
console.log(1+p);//1[object Object]
//5.valueOf
function Person1(){
this.valueOf = function () {
return 123;
}
}
var p1 = new Person1();
console.log(1 + p1);//124
//在對象參與運(yùn)算的時(shí)候
// 1.默認(rèn)的會先去調(diào)用對象的valueOf方法帝雇,
// 2.如果valueOf獲取到的值,無法進(jìn)行運(yùn)算 蛉拙,就去去調(diào)用p的toString方法 最終做的就是字符串拼接的工作
//6. __proto__
//原型對象對象中的屬性
//可以使用 對象.__proto__ 去訪問原型對象
</script>