寫在之前
距離上次整理日記整整一個周了溺忧,本來想再上周把這部分的內容完成咏连,兌現(xiàn)之前吹過的牛皮。遲遲沒有寫一是因為上周項目比較忙鲁森,另一個是這一部分真的不好寫祟滴,我寫著寫著感覺這一塊的內容都不確定了(自己的基礎真的有點差),去驗證自己的想法的時候花了不少時間歌溉。這次的內容以《Javascript高級程序設計》第六章為主垄懂,先說原型與原型鏈,再說常用的繼承的方式以及優(yōu)缺點痛垛,最后結合實例看原型與繼承在網頁編程中的實際應用草慧。
原型基礎
原型對象
Javascript是面向對象的語言,在JS的世界里面萬物皆對象匙头。每個對象都有一個原型 prototype
對象漫谷,通過函數創(chuàng)建的對象也將擁有這個原型對象。原型是一個指向對象的指針蹂析。
- 可以將原型理解為對象的父親抖剿,對象從原型對象繼承屬性。
- 原型就是對象识窿,除了是某個對象的父母之外沒什么特別之處斩郎。
- Object是所有對象的爸爸(爺爺,祖宗)喻频,所以對象都可以使用
toString/toValues/isPrototypeOf
等方法(子代可以使用父輩的資產)缩宜。 - 使用原型可以解決,通過構造函數創(chuàng)造對象的時復制多個函數造成的內存占用問題。
- 原型包含
constructor
屬性锻煌,指向構造函數 - 對象包含
__proto__
指向他的原型對象
下例就是使用數組原型對象的concat
方法完成的連接操作妓布。
let arr = ["a"];
console.log(arr.concat("b"));
如果 console.dir(arr)
我們就會發(fā)現(xiàn)他的結構信息
可以看到__proto__
指向原型對象,原型對象包含 constructor
它指向構造函數宋梧。上面的內容可以 arr.__proto__ 指向 Array.prototype 即 arr.__proto__ === Array.protptype
和 Array.prototype.constructor 指向 Array()
默認情況下創(chuàng)建的對象都是有原型的匣沼。obj的原型都為元對象Object,可以用 Object.getPrototypeOf(obj)
檢驗捂龄,它會返回 true释涛。
我們也可以創(chuàng)建一個極簡對象(純數據字典對象),沒有原型(原型為null)
let obj = Object.create(null,{
name:{
value:'zhangsan'
}
})
console.log(obj.hasOwnProperty('name')); //Error
prototype
用于實例對象使用倦沧,__prototype__
用于函數對象使用唇撬。JS萬物皆對象,構造函數User 本身就是一個對象展融,通過構造函數User new出來的對象就是構造函數User的實例對象窖认。
function User() {}
User.__proto__.view = function() {
console.log("User function view method");
};
User.view(); //User function view method
User.prototype.show = function() {
console.log("zhansan");
};
let user = new User();
user.show(); // zhangsan
console.log(User.prototype === user.__proto__); //true
下面是原型關系分析圖
下面是使用構造函數床架對象的原型體現(xiàn)
- 構造函數擁有原型
-
創(chuàng)建對象時構造函數把原型對象賦予實例對象
function User() {}
let user = new User();
console.log(user.__proto__ === User.prototype); // true
下面使用數組會產生多級繼承
let arr = [];
console.log(arr.__proto__ === Array.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype); //true
可以使用
setPrototypeOf
與 getPrototypeOf
獲取與設置原型
let child = {};
let parent = {name:'parent'}
Object.setPrototype(child,parent);
console.log(Object.getPrototypeOf(child); //指向parent
下面使用自動構造函數創(chuàng)建的對象的原型體現(xiàn)可以參考圖一。
function User(){}
let user = new User();
上面說到構造函數存在于原型對象中告希,是指向構造函數的引用
function User() {
this.show = function() {
return "show method";
};
}
const obj = new User(); //true
console.log(obj instanceof User); //true
console.log(obj.__proto__.constructor === User); //true
console.log(User.prototype.constructor === User); //true
const obj2 = new obj.constructor();
console.dir(obj2.show()); //show method
既然 constructor 是指向構造函數的引用扑浸,那我們就可以使用 constructor 創(chuàng)建對象
function User(name, age) {
this.name = name;
this.age = age;
}
function createByObject(obj, ...args) {
const constructor = Object.getPrototypeOf(obj).constructor;
return new constructor(...args);
}
let obj1 = new User("sinochem");
let obj2 = createByObject(obj1, "sinochemtech", 12);
console.log(xj);
原型鏈
通過引用類型的原型,繼承另一個引用類型的屬性和方法燕偶,這個也是實現(xiàn)繼承的步驟
使用Object.setPrototypeOf可是設置對象的原型喝噪,下面的示例中繼承關系為 obj > child > parent。
Object.getPrototype 用于獲取一個對象的原型杭跪。
let obj = {
name: "zhangsan"
};
let child = {
ccc: "child-child"
};
let parent = {
ppp: "parent-parent"
};
//讓obj繼承hd,即設置obj的原型為hd
Object.setPrototypeOf(obj, child);
Object.setPrototypeOf(child, parent);
console.log(obj.ccc); // child-child
console.log(Object.getPrototypeOf(child) == parent); //true
原型檢測
instanceof 檢測構造函數的 prototype
屬性是否出現(xiàn)在某個實例對象的原型鏈上
function A() {}
function B() {}
function C() {}
const c = new C();
B.prototype = c;
const b = new B();
A.prototype = b;
const a = new A();
console.dir(a instanceof A); //true
console.dir(a instanceof B); //true
console.dir(a instanceof C); //true
console.dir(b instanceof C); //true
console.dir(c instanceof B); //false
使用isPrototypeOf
檢測一個對象是否在另一個對象的原型鏈中
const a = {};
const b = {};
const c = {};
Object.setPrototypeOf(a, b);
Object.setPrototypeOf(b, c);
console.log(b.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(b)); //true
屬性遍歷
使用in
檢測原型鏈上是否存在屬性驰吓,使用 hasOwnProperty
只檢測當前對象
let a = { url: "baidu.com" };
let b = { name: "百度" };
Object.setPrototypeOf(a, b);
console.log("name" in a);
console.log(a.hasOwnProperty("name")); //true
console.log(a.hasOwnProperty("url")); //true
使用 for/in
遍歷時同時會遍歷原型上的屬性如下例:
let parent = { name: "zhangsan" };
let child = Object.create(parent, {
url: {
value: "www.baidu.com",
enumerable: true
}
});
for (const key in child) {
console.log(key);
}
hasOwnProperty
方法判斷對象是否存在屬性涧尿,而不會查找原型。所以如果只想遍歷對象屬性使用以下代碼:
let parent = { name: "后盾人" };
let child = Object.create(parent, {
url: {
value: "baidu.com",
enumerable: true
}
});
for (const key in child) {
if (child.hasOwnProperty(key)) {
console.log(key);
}
}
借用原型
使用 call
或 apply
可以借用其他原型方法完成功能檬贰。
下面的bar對象不能使用max
方法姑廉,但可以借用 foo 對象的原型方法
let foo = {
data: [1, 2, 3, 4, 5]
};
Object.setPrototypeOf(foo, {
max: function(data) {
return data.sort((a, b) => b - a)[0];
}
});
console.log(hd.max(hd.data));
let bar = {
lessons: { js: 100, php: 78, node: 78, linux: 125 }
};
console.log(foo.__proto__.max.call(bar, Object.values(bar.lessons)));
因為 Math.max
就是獲取最大值的方法,所以代碼可以再次簡化
let foo = {
data: [1, 2, 3, 4, 5]
};
console.log(Math.max.apply(null, Object.values(foo.data)));
let bar = {
lessons: { js: 100, php: 78, node: 78, linux: 125 }
};
console.log(Math.max.apply(bar, Object.values(bar.lessons)));
下面是獲取設置了 class
屬性的按鈕翁涤,但DOM節(jié)點不能直接使用數組的filter
等方法桥言,但借用數組的原型方法就可以操作了。
<body>
<button message="foo" class="red">FOO</button>
<button message="bar">BAR</button>
</body>
<script>
let btns = document.querySelectorAll("button");
btns = Array.prototype.filter.call(btns, item => {
return item.hasAttribute("class");
});
</script>
原型總結
函數也是對象葵礼,但是比較特殊号阿,有多個原型(__proto__
與prototype
)兩個一定要分清楚。
通常說為構造函數設置原型指的是設置 prototype
當使用構造函數創(chuàng)建對象時把這個原型賦給這個對象鸳粉。
function User(name) {
this.name = name;
}
User.prototype = {
show() {
return this.name;
}
};
let user = new User("zhangsan");
console.log(user.show());
函數默認prototype
指包含一個屬性 constructor
的對象扔涧,constructor
指向當前構造函數
function User(name) {
this.name = name;
}
let user = new User("向軍");
console.log(uer);
console.log(User.prototype.constructor == User); //true
console.log(user.__proto__ == User.prototype); //true
let lisi = new user.constructor("李四");
console.log(lisi.__proto__ == user.__proto__); //true
原型中保存引用類型會造成對象共享屬性,所以一般只會在原型中定義方法。
function User() {}
User.prototype = {
lessons: ["JS", "VUE"]
};
const lisi = new User();
const wangwu = new User();
lisi.lessons.push("CSS");
console.log(lisi.lessons); //["JS", "VUE", "CSS"]
console.log(wangwu.lessons); //["JS", "VUE", "CSS"]
為Object原型對象添加方法枯夜,將影響所有函數
<body>
<button onclick="this.hide()">BUTTON</button>
</body>
<script>
Object.prototype.hide = function() {
this.style.display = "none";
};
</script>
了解了原型后可以為系統(tǒng)對象添加方法弯汰,比如為字符串添加了一截斷函數。
String.prototype.truncate = function (len = 5) {
return this.length <= len ? this : this.substr(0, len) + '...';
}
console.log('1234567890'.truncate(3)); //123...
使用 Object.create
創(chuàng)建一個新對象時使用現(xiàn)有對象做為新對象的原型對象湖雹。它第一個參數必須是一個對象或者null(沒有原型的對象)咏闪。
使用Object.create
設置對象原型
let user = {
show() {
return this.name;
}
};
let obj = Object.create(user);
obj.name = "zhangsan";
console.log(obj.show());
在設置時使用第二個參數設置新對象的屬性
let user = {
show() {
return this.name;
}
};
let obj = Object.create(user, {
name: {
value: "后盾人"
}
});
console.log(obj);
在實例化對象上存在__proto__
記錄了原型,所以可以通過對象訪問到原型的屬性或方法摔吏。
-
__proto__
不是對象屬性鸽嫂,理解為prototype
的getter/setter
實現(xiàn),他是一個非標準定義 -
__proto__
內部使用getter/setter
控制值舔腾,所以只允許對象或null - 建議使用
Object.setPrototypeOf
與Object.getProttoeypOf
替代__proto__
下面修改對象的 __proto__
是不會成功的溪胶,因為_proto__
內部使用getter/setter
控制值,所以只允許對象或null稳诚。
let obj = {};
obj.__proto__ = "123";
console.log(obj);
下面定義的__proto__
就會成功哗脖,因為這是一個極簡對象,沒有原型對象所以不會影響__proto__
賦值扳还。
let obj = Object.create(null);
obj.__proto__ = "123";
console.log(obj); //{__proto__: "123"}
下面通過改變對象的 __proto__
原型對象來實現(xiàn)繼承才避,繼承可以實現(xiàn)多層,
let person = {
name: "zhangsan"
};
let bar = {
show() {
return this.name;
}
};
let foo = {
handle() {
return `用戶: ${this.name}`;
}
};
bar.__proto__ = foo;
person.__proto__ = bar;
console.log(person.show());
console.log(person.handle());
console.log(person);
構造函數中的 __proto__
使用
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
return `姓名:${this.name},年齡:${this.age}`;
};
let lisi = new User('李四', 12);
let wangwu = new User('王武', 16);
console.log(lisi.__proto__ == User.prototype); //true
可以使用 __proto__
或 Object.setPrototypeOf
設置對象的原型氨距,使用Object.getProttoeypOf
獲取對象原型桑逝。
function Person() {
this.getName = function() {
return this.name;
};
}
function User(name, age) {
this.name = name;
this.age = age;
}
let lisi = new User("李四", 12);
Object.setPrototypeOf(lisi, new Person());
console.log(lisi.getName()); //李四
對象設置屬性,只是修改對象屬性并不會修改原型屬性俏让,使用hasOwnProperty
判斷對象本身是否含有屬性并不會檢測原型楞遏。
function User() {}
const lisi = new User();
const wangwu = new User();
lisi.name = "李四";
console.log(lisi.name); //李四
console.log(lisi.hasOwnProperty("name")); //true
//修改原型屬性后
lisi.__proto__.name = "張三";
console.log(wangwu.name); //張三
//刪除對象屬性后
delete lisi.name;
console.log(lisi.hasOwnProperty("name")); //false
console.log(lisi.name); // 張三
使用 in
會檢測原型與對象,而 hasOwnProperty
只檢測對象首昔,所以結合后可判斷屬性是否在原型中
使用建議:通過前介紹我們知道可以使用多種方式設置原型
-
prototype
構造函數的原型屬性 -
Object.create
創(chuàng)建對象時指定原型 -
__proto__
聲明自定義的非標準屬性設置原型寡喝,解決之前通過Object.create
定義原型,而沒提供獲取方法 -
Object.setPrototypeOf
設置對象原型
這幾種方式都可以管理原型勒奇,一般以我個人情況來講使用 prototype
更改構造函數原型预鬓,使用 Object.setPrototypeOf
與 Object.getPrototypeOf
獲取或設置原型。
構造函數
原型屬性
構造函數在被new
時把構造函數的原型(prototype)賦值給新對象赊颠。如果對象中存在屬性將使用對象屬性格二,不再原型上查找方法。
構造函數只會產生一個原型對象
function foo() {
this.show = function() {
return "show in object";
};
}
foo.prototype.show = function() {
return "show in prototype";
};
const obj = new foo();
console.log(obj.show());
對象的原型引用構造函數的原型對象竣蹦,是在創(chuàng)建對象時確定的顶猜,當構造函數原型對象改變時會影響后面的實例對象。
function foo() {}
foo.prototype.name = "foofoo";
const obj1 = new foo();
console.log(obj1.name); //foofoo
foo.prototype = {
name: "123"
};
const obj2 = new hd();
console.dir(obj2.name); //123
以下代碼直接設置了構造函數的原型將造成 constructor
丟失
function User(name) {
this.name = name;
}
User.prototype = {
show: function() {}
};
let u1 = new User("u1u1");
let u2 = new u1.constructor("u2u2");
console.log(u2); //String {"u2u2"}
正確的做法是要保證原型中的 constructor
指向構造函數
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
show: function() {}
};
let u1 = new User("u1u1");
let u2 = new hd.constructor("u2u2");
console.log(u2);
構造函數的優(yōu)化使用
使用構造函數會產生函數復制造成內存占用痘括,及函數不能共享的問題驶兜。
function User(name) {
this.name = name;
this.get = function() {
return this.name;
};
}
let lisi = new User("小明");
let wangwu = new User("王五");
console.log(lisi.get == wangwu.get); //false
將方法定義在原型上為對象共享,解決通過構造函數創(chuàng)建對象函數復制的內存占用問題
function User(name) {
this.name = name;
}
User.prototype.get = function() {
return "姓名:" + this.name;
};
let lisi = new User("小明");
let wangwu = new User("王五");
console.log(lisi.get == wangwu.get); //true
//通過修改原型方法會影響所有對象調用,因為方法是共用的
lisi.__proto__.get = function() {
return "姓名123:" + this.name;
};
console.log(lisi.get());
console.log(wangwu.get());
下面演示使用原型為多個實例共享屬性
function User(name, age) {
this.name = name;
this.age = age;
this.show = () => {
return `你在${this.site}的姓名:${this.name}抄淑,年齡:${this.age}`;
}
}
User.prototype.site = '中化';
let lisi = new User('李四', 12);
let xiaoming = new User('小明', 32);
console.log(lisi.show()); //你在中化的姓名:李四屠凶,年齡:12
console.log(xiaoming.show()); //你在中化的姓名:小明,年齡:32
使用Object.assign
一次設置原型方法來復用肆资,后面會使用這個功能實現(xiàn)Mixin模式
function User(name, age) {
this.name = name;
this.age = age;
}
Object.assign(User.prototype, {
getName() {
return this.name;
},
getAge() {
return this.age;
}
});
let lisi = new User('李四', 12);
let xiaoming = new User('小明', 32);
console.log(lisi.getName()); //李四
console.log(lisi.__proto__)
通過上面這種方法設置可以避《JavaScript高級程序設計》提到了通過原型字面量批量設置原型帶來的構造函數指針問題和如果在字面量設置原型之前在原型上設置方法的方法丟失問題(這是一個順序問題矗愧,因為原型指向了新對象,原原型對象就不再生效了)郑原。如果想了解那一部分的內容還是建議讀一下高級編程這本書唉韭。
繼承
體驗繼承
下面為 Stu
更改了原型為User
的實例對象,lisi
是通過構造函數Stu
創(chuàng)建的實例對象
-
lisi
在執(zhí)行getName
方法時會從自身并向上查找原型犯犁,這就是原型鏈特性 - 當然如果把
getName
添加到對象上属愤,就不繼續(xù)追溯原型鏈了
function User() {}
User.prototype.getName = function() {
return this.name;
};
function Stu(name) {
this.name = name;
}
Stu.prototype = new User();
const lisi = new Stu("李四");
console.log(lisi.__proto__);
console.log(lisi.getName());
當對象中沒使用的屬性時,JS會從原型上獲取這就是繼承在JavaScript中的實現(xiàn)酸役。
繼承實現(xiàn)
下面使用Object.create
創(chuàng)建對象住诸,做為Admin、Member
的原型對象來實現(xiàn)繼承涣澡。
function User() {}
User.prototype.getUserName = function() {};
function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};
function Member() {}
Member.prototype = Object.create(User.prototype);
Member.prototype.email = function() {};
console.log(new Admin());
console.log(new Member());
不能使用以下方式操作贱呐,因為這樣會改變User的原型方法,這不是繼承入桂,這是改變原型
...
function User() {}
User.prototype.getUserName = function() {};
function Admin() {}
Admin.prototype = User.prototype;
Admin.prototype.role = function() {};
...
上一節(jié)提到有多種方式通過構造函數創(chuàng)建對象
function Admin() {}
console.log(Admin == Admin.prototype.constructor); //true
let obj1 = new Admin.prototype.constructor();
console.log(obj1);
let obj2 = new Admin();
console.log(obj2);
因為有時根據得到的對象獲取構造函數奄薇,然后再創(chuàng)建新對象所以需要保證構造函數存在,但如果直接設置了 Admin.prototype
屬性會造成constructor
丟失抗愁,所以需要再次設置constructor
值馁蒂。
function User() {}
function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};
let obj1 = new Admin();
console.log(obj1.constructor); //constructor丟失,返回User構造函數
Admin.prototype.constructor = Admin;
let obj2 = new Admin();
console.log(obj2.constructor); //正確返回Admin構造函數
//現(xiàn)在可以通過對象獲取構造函數來創(chuàng)建新對象了
console.log(new obj2.constructor());
上面通過顯示的方式指定了 constructor
導致成了 constructor
成為了可遍歷的屬性蜘腌,我們可以使用Object.defineProperty
定義來禁止遍歷constructor屬性沫屡。
function User() {}
function Admin(name) {
this.name = name;
}
Admin.prototype = Object.create(User.prototype);
Object.defineProperty(Admin.prototype, "constructor", {
value: Admin,
enumerable: false //禁止遍歷
});
let hd = new Admin("偉哥有話說");
for (const key in hd) {
console.log(key);
}
剛次提到這種繼承方式就是《JavaScript高級程序編程》中提到寄生組合繼承。該繼承方式是開發(fā)人員認為最理想的繼承方式逢捺。
function SuperType(name){
this.name = name;
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function(){
alert(this.name)
};
function SubType(name,age){
SubType.call(this,name);
this.age = age;
}
// 繼承方法
SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
let instance = new SubType('Nocholas',26)
instance.sayName();
instance.sayAge();
對于這種繼承可以如下封裝:
function inheritPrototype(subType, superType){
const prototype = Object.create(superType);
prototype.constructor = subType;
subType.prototype = prototype;
}
看過《JavaScript高級程序編程》的同學可能覺得這個上面這個封裝及熟悉又陌生谁鳍。這個就是你覺得的那個癞季,變得就是Object.create
劫瞳。其實這個就是書上的實現(xiàn)。冷不丁的是不是又搞清楚了一個原理??绷柒。下面這個寫法更簡單志于。
function inheritPrototype(subType, superType){
subType.prototype = Object.create(superType);
subType.prototype.constructor = subType;
}
方法重寫
下而展示的是子類需要重寫父類方法的技巧。
function Person() {}
Person.prototype.getName = function() {
console.log("parent method");
};
function User(name) {}
User.prototype = Object.create(Person.prototype);
User.prototype.constructor = User;
User.prototype.getName = function() {
//調用父級同名方法
Person.prototype.getName.call(this);
console.log("child method");
};
let instance = new User();
instance.getName();
多態(tài)
根據多種不同的形態(tài)產生不同的結果废睦,下而會根據不同形態(tài)的對象得到了不同的結果伺绽。
function User() {}
User.prototype.show = function() {
console.log(this.description());
};
function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.description = function() {
return "管理員在此";
};
function Member() {}
Member.prototype = Object.create(User.prototype);
Member.prototype.description = function() {
return "我是會員";
};
function Enterprise() {}
Enterprise.prototype = Object.create(User.prototype);
Enterprise.prototype.description = function() {
return "企業(yè)帳戶";
};
for (const obj of [new Admin(), new Member(), new Enterprise()]) {
obj.show();
}
實例操作
使用 call/apply
制作選項卡
<!DOCTYPE html>
<html>
<head>
<title>組合繼承</title>
<style>
* {
padding: 0;
margin: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
main {
width: 400px;
flex-direction: column;
position: relative;
margin-right: 20px;
}
main nav {
display: flex;
height: 50px;
align-items: center;
}
main nav a {
background: #95a5a6;
margin-right: px;
padding: 10px 20px;
border: solid 1px #333;
color: #fff;
text-decoration: none;
}
main nav a:first-of-type {
background: #e67e22;
}
section {
height: 200px;
width: 100%;
background: #f1c40f;
position: absolute;
font-size: 2em;
display: none;
}
.hd-tab section:first-of-type {
display: block;
}
section:nth-child(even) {
background: #27ae60;
}
</style>
</head>
<body>
<main class="tab1">
<nav>
<a href="javascript:;">用戶管理</a>
<a href="javascript:;">配置管理</a>
</nav>
<section>用戶管理</section>
<section>配置管理</section>
</main>
<main class="tab2">
<nav>
<a href="javascript:;">用戶管理</a>
<a href="javascript:;">配置管理</a>
</nav>
<section>用戶管理</section>
<section>配置管理</section>
</main>
<script type="text/javascript">
//繼承工廠
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype);
sub.prototype.constructor = sub;
}
//動作類
function Animation() {}
Animation.prototype.show = function() {
this.style.display = "block";
};
//隱藏所有元素
Animation.prototype.hide = function() {
this.style.display = "none";
};
//必變元素集合背景
Animation.prototype.background = function(color) {
this.style.background = color;
};
//選項卡類
function Tab(tab) {
this.tab = tab;
this.links = null;
this.sections = null;
}
extend(Tab, Animation);
Tab.prototype.run = function() {
this.links = this.tab.querySelectorAll("a");
this.sections = this.tab.querySelectorAll("section");
this.bindEvent();
this.action(0);
};
//綁定事件
Tab.prototype.bindEvent = function() {
this.links.forEach((el, i) => {
el.addEventListener("click", () => {
this.reset();
this.action(i);
});
});
};
//點擊后觸發(fā)動作
Tab.prototype.action = function(i) {
this.background.call(this.links[i], "#e67e22");
this.show.call(this.sections[i]);
};
//重置link與section
Tab.prototype.reset = function() {
this.links.forEach((el, i) => {
this.background.call(el, "#95a5a6");
this.hide.call(this.sections[i]);
});
};
new Tab(document.querySelector(".tab1")).run();
new Tab(document.querySelector(".tab2")).run();
</script>
</body>
</html>
寫在最后
這一篇終于寫完了。。感覺對JS這個語言重新熟悉了一遍奈应。ES6逐漸普及澜掩,新式的語法讓繼承變得很簡單,似乎這些東西不用我們再去考慮了杖挣。但是理解原理會對這門語言更加深刻肩榕。JavaScript相對其他編程語言來講,靈活的難以想象惩妇,給我一個對象株汉,就能還你一個世界。