一、call、apply、bind
(1) call
call:在調(diào)用執(zhí)行函數(shù)時,可以修改函數(shù)內(nèi)部this指向,同時給函數(shù)傳遞序列數(shù)據(jù)
// 對象1
var tom = {
name: "湯姆",
eat: function(food1, food2) {
console.log(this.name + "吃飯了:", food1, food2)
}
}
// 對象2
var jerry = {
name: "杰瑞"
}
// 正常調(diào)用函數(shù)
tom.eat("烤全羊", "小龍蝦")
// jerry對象借用tom對象的eat()函數(shù)
// call的第一個參數(shù):修改this指向
// call的后面的參數(shù):傳遞給函數(shù)需要實際參數(shù)
tom.eat.call(jerry, "燒烤", "扎啤")
Object.prototype.toString.call(null) // [object Null]
(2) apply
apply:在調(diào)用執(zhí)行函數(shù)時,修改函數(shù)內(nèi)部this指向,同時傳遞給函數(shù)包裝成數(shù)組的實際參數(shù)
// 對象1
var tom = {
name: "湯姆",
eat: function(food1, food2) {
console.log(this.name + "吃飯了:", food1, food2)
}
}
// 對象2
var jerry = {
name: "杰瑞"
}
// 正常調(diào)用函數(shù)
tom.eat("烤全羊", "小龍蝦")
// jerry對象借用tom對象的eat()函數(shù)
// apply第一個參數(shù):修改this指向的實際對象
// apply第二個參數(shù):數(shù)組,包含了要傳遞給函數(shù)執(zhí)行的所有數(shù)據(jù)
tom.eat.apply(jerry, ["大閘蟹", "帝王蟹"])
Math.max.apply(null, [1,3,4,56,7,7,32,2,4]) // 56
(3) bind
bind:在調(diào)用執(zhí)行函數(shù)的時候,修改函數(shù)內(nèi)部this指向,并不立即執(zhí)行函數(shù)而是返回了函數(shù)的應(yīng)用;可以讓開發(fā)人員后續(xù)調(diào)用執(zhí)行函數(shù)
// 對象1
var tom = {
name: "湯姆",
eat: function(food1, food2) {
console.log(this.name + "吃飯了:", food1, food2)
}
}
// 對象2
var jerry = {
name: "杰瑞"
}
// 正常調(diào)用函數(shù)
tom.eat("烤全羊", "小龍蝦")
// jerry對象借用tom對象的eat()函數(shù)
// bind第一個參數(shù);修改了this指向的對象
// bind后面的參數(shù):傳遞給函數(shù)需要的實際參數(shù)
// 注意:bind不會讓函數(shù)立即執(zhí)行,返回函數(shù)聲明的引用,讓開發(fā)人員可以延遲執(zhí)行函數(shù)
// var fn = tom.eat.bind(jerry, "小龍蝦", "啤酒")
// fn()
tom.eat.bind(jerry, "小龍蝦", "啤酒")()
(4)call、apply、bind函數(shù)的區(qū)別
call/apply/bind都是輔助函數(shù),可以用來修改正在執(zhí)行的函數(shù)內(nèi)部this指向
call修改this指向,通過數(shù)據(jù)序列傳遞給函數(shù)實際參數(shù),如tom.eat.call(jerry, '數(shù)據(jù)a', '數(shù)據(jù)b')
面向?qū)ο笾?,我們可以通過構(gòu)造函數(shù)(聲明類型)的方式對需要的數(shù)據(jù)進行封裝管理,方便后期對文章對象數(shù)據(jù)的使用 (3)繼承 (4) 原型繼承 (5) 冒充繼承 (6) 組合繼承 (7) 寄生組合繼承 (8) 多態(tài) 類比生活:狀態(tài)的改變, 行為的改變,apply修改this指向,通過數(shù)組方式傳遞個函數(shù)實際參數(shù),如tom.eat.apply(jerry, ['數(shù)據(jù)a', '數(shù)據(jù)b'])
bind修改this指向,通過數(shù)據(jù)序列方式傳遞給函數(shù)實際參數(shù),同時返回函數(shù)的聲明,讓開發(fā)人員可以延遲或者立即調(diào)用,擴展了使用范圍,如:tom.eat.bind(jerry, '數(shù)據(jù)a', '數(shù)據(jù)b')()
二、面向?qū)ο筮M階
(1) 面向?qū)ο?/strong>
面向?qū)ο蟀筇卣鳎?/p>
(2) 封裝
開發(fā)一個博客網(wǎng)站,需要處理文章數(shù)據(jù),原始的文章數(shù)據(jù)管理方式:var title = "標(biāo)題"
var content = "文章內(nèi)容"
var publish = new Date() // 發(fā)表時間
var read_cnt = 0 // 瀏覽次數(shù)
var liked_cnt = 0 // 點贊次數(shù)
var collect_cnt = 0 // 收藏次數(shù)
function Article(title, content, publish, read_cnt, liked_cnt, collect_cnt) {
this.title = title
this.content = content
this.publish = publish
this.read_cnt =read_cnt
this.liked_cnt = liked_cnt
this.collect_cnt = collect_cnt
}
// 創(chuàng)建一個新的文章
var article = new Article('標(biāo)題', '內(nèi)容', new Date(), 0, 0, 0)
// 需要再發(fā)表一篇文章
var article2 = new Article('標(biāo)題2', '內(nèi)容2', new Date(), 0, 0, 0)
// 將文章數(shù)據(jù)通過構(gòu)造函數(shù)封裝起來,方便使用和維護
// 1、創(chuàng)建兩個對象
var tom = new Object()
tom.name = "湯姆"
tom.play = function() {
console.log(this.name, "正在游戲中...")
}
var jerry = new Object()
jerry.name = "杰瑞"
// 2、使用對象的屬性和方法
// console.log(tom.name)
// console.log(jerry.name)
// tom.play()
// jerry.play()
// 3、如果想讓一個數(shù)據(jù)或者函數(shù),被當(dāng)前類型所有對象使用
// 可以將屬性或者函數(shù),掛載到類型的原型對象上
// 通過Object創(chuàng)建的對象,都可以訪問原型上的數(shù)據(jù)和方法
Object.prototype.maxAge = 120
Object.prototype.study = function() {
console.log("goodgoodsstudy daydayup")
}
console.log(tom.name, tom.maxAge)
console.log(jerry.name, jerry.maxAge)
tom.study()
jerry.study()
// -------------------------------
// 4、構(gòu)造函數(shù)創(chuàng)建對象
function Student(name, age) {
this.name = name // 當(dāng)前對象的成員屬性,獨立屬性
this.age = age
}
// 原型對象上掛載公共數(shù)據(jù)
Student.prototype.classRoom = "1701教室"
Student.prototype.study = function() {
console.log("..............")
}
var shuke = new Student("舒克", 28)
var beita = new Student("貝塔", 22)
console.log(shuke.name, shuke.age, shuke.classRoom)
console.log(beita.name, beita.age, beita.classRoom)
原型繼承,基于原型對象的繼承,子類和父類在原型鏈上就存在繼承關(guān)系;存在的問題是子類在創(chuàng)建對象時無法直接修改父類的屬性數(shù)據(jù),必須在創(chuàng)建的對象上通過訪問屬性的方式進行修改,讓編碼變得復(fù)雜;開發(fā)中一般很少獨立使用function Person() {}
function Student() {}
// 原型繼承
Student.prototype = new Person()
冒充繼承,基于call()輔助函數(shù)修改this指向的功能,在子類中通過調(diào)用父類構(gòu)造函數(shù)的call()方法,完成子類可以使用父類構(gòu)造函數(shù)內(nèi)部屬性和方法的使用;存在的問題是首先在原型鏈上沒有真實繼承關(guān)系,所以子類無法繼承父類在原型對象上的數(shù)據(jù)和函數(shù)function Person(name) {...}
function Student(name, age) {
// 冒充繼承,可以直接修改父類屬性
Person.call(this, name)
this.age = age
}
組合繼承,結(jié)合了原型繼承和冒充繼承,通過原型繼承讓子類可以使用父類原型對象上的屬性和函數(shù),通過冒充繼承可以讓子類直接訪問父類屬性,優(yōu)化操作語法;存在的問題是兩種繼承方式都訪問了父類的構(gòu)造函數(shù),繼承過程中造成父類構(gòu)造函數(shù)的多次調(diào)用存在一定的性能問題;如果性能沒有太高要求的情況下,可以通過組合繼承的方式進行實現(xiàn)function Person(name) {...}
function Student(name, age) {
// 冒充繼承
Person.call(this, name)
this.age = age
}
// 原型繼承
Student.prototype = new Person('')
寄生組合繼承,對組合繼承的一種升級,將父類原型對象進行了復(fù)制得到一個空對象,子類在冒充繼承的繼承上,實現(xiàn)原型繼承時繼承這個空對象,就不需要讓父類的構(gòu)造函數(shù)再次調(diào)用,這個空對象的創(chuàng)建和調(diào)用性能消耗較低,對性能上有一定的優(yōu)化提升;項目中如果出現(xiàn)了對繼承關(guān)系的頻繁使用,建議可以將寄生組合繼承進行封裝使用,優(yōu)化項目性能function Person(name) {...}
function Student(name, age) {
// 冒充繼承
Person.call(this, name)
this.age = age
}
// 寄生函數(shù)
function inherit(Parent, Child) {
var Temp = function() {} // 寄生構(gòu)造函數(shù)|空函數(shù)
Temp.prototype = Parent.prototype // 復(fù)制 原型對象
Child.prototype = new Temp() // 原型繼承(空構(gòu)造函數(shù),性能優(yōu)化)
Child.prototype.constructor = Child // 指定自己構(gòu)造函數(shù)
}
// 寄生繼承:讓Student繼承自Parent類型
inherit(Person, Student)
多態(tài):描述了一個對象在使用過程中狀態(tài)和行為的變化;一般開發(fā)中慢慢體驗!tom在公司-員工、回家到兒子-爸爸、到父親-兒子,這樣角色狀態(tài)的變化,就是一種多態(tài)交通工具有一個移動的行為,自行車移動-騎行行為、公交車移動-駕駛行為、飛機移動-飛行行為