問題1: apply缆瓣、call 、bind有什么作用材泄,什么區(qū)別
在實際編程過程中沮焕,this的動態(tài)切換很容易讓人混淆它的指向,為了把this固定下來拉宗,避免意想不到的情況峦树,javascript提供了apply,call旦事,bind三種方法來切換/ 固定this的指向魁巩。
function.prototype.apply()
函數實例的call方法,可以指定該函數內部this的指向姐浮,即函數執(zhí)行時所在的作用域谷遂,然后在所指定的作用域中,調用該函數
var obj = {}
function foo(){
return this
}
foo() === this //true
foo.call(obj) === obj //true
上面代碼中卖鲤,在全局環(huán)境運行函數foo時肾扰,this指向全局環(huán)境;call方法可以改變this的指向扫尖,指定this指向對象obj白对,然后
在對象obj的作用域中運行函數foo
call方法的參數,應該是一個對象换怖。如果參數為空甩恼、null和undefined,則默認傳入全局對象沉颂。
var n = 123
var obj = {n: 456}
function a(){
console.log(this.n)
}
a.call() // 123
a.call(null)//123
a.call(undefined)//123
a.call(window)//123
a.call(obj) // 456
如果call方法的參數是一個原始值条摸,那么這個原始值會自動轉成對應的包裝對象,然后傳入call方法
var f = function(){
return this
}
f.call(5)
5是一個原始值铸屉,他的包裝對象為Number钉蒲,相當于f.call(Number),返回Number{[[primitive]] : 5}
call方法還可以接受多個參數。
func.call(thisValue,arg1,arg2,...)
call的第一個參數就是this所要指向的對象彻坛,后面的參數則是函數調用時所需的參數顷啼。
function add(a,b){
return a+b
}
add.call(this,1,2) // 3
call方法的一個應用是調用對象的原生方法。
var obj = {}
obj.hasOwnProperty('toString')//false
//覆蓋掉繼承的hasOwnProperty方法
obj.hasOwnProperty = function(){
return true
}
obj.hasOwnProperty('toString')//true昌屉,傳入任何參數都返回true
Object.prototype.hasOwnproperty.call(obj,'toString') // false
function.prototype.apply()
apply方法的作用與call類似钙蒙,也是改變this的指向,然后再調用該函數间驮。唯一的區(qū)別是躬厌,它接收一個數組作為函數執(zhí)行時
的參數,使用格式如下
func.apply(thisValue,[arg1,arg2,...])
利用這一點竞帽,可以做一些有用的應用扛施。
1. 找出數組最大元素
javascript不提供找出數組最大元素的函數鸿捧。結合使用apply方法和Math.max方法,就可以返回數組的最大元素.
var a = [2,4,7,10,56]
Math.max.apply(null,a)//56
2.轉換類似數組的對象
利用數組對象的slice方法疙渣,可以將一個類似數組的對象(比如arguments對象)轉為真正的數組匙奴。
Array.prototype.slice.apply({0:1,length:1})//[1],類數組對象有個length的屬性
Array.prototype.slice.apply({0:1})//[]昌阿,沒有l(wèi)ength屬性饥脑,默認不被識別為類數組對象,返回空數組
function.prototype.bind()
bind方法用于將函數體內的this綁定到某個對象懦冰,然后返回一個新函數灶轰。
var d = new Date()
d.getTime() // 1491058448289
var print = d.getTime
print()
上面代碼中,我們將d.getTime方法賦給變量print刷钢,然后調用print就報錯了笋颤。這是因為getTime方法內部的this,綁定
Date對象的實例内地,當把getTime方法賦給變量print以后伴澄,在全部環(huán)境中調用print函數,內部的this已經不指向Date對象的實例了
bind方法可以解決這個問題阱缓,讓log方法綁定console對象非凌。
var print = d.getTime.bind(d)
print() //1491058927995
下面是一個更清晰的例子
var counter = {
count: 0,
inc: function(){
this.count++
}
}
counter.count // 0
counter.inc()
counter.count // 1
上面代碼中,counter.inc內部的this荆针,默認指向counter對象敞嗡,如果將這個方法賦值給另一個變量,就會報錯
var counter = {
count: 0,
inc: function(){
this.count++
}
}
var func = counter.inc // 相當于 var func = function(){this.count++}
func()
counter.count // 0
count // NaN全局環(huán)境中并沒有count這個變量
上面代碼中航背,函數func是在全局環(huán)境中運行的喉悴,這時inc內部的this指向頂層對象window,所以counter.count是不會
變的玖媚,反而創(chuàng)建了一個全局變量count箕肃。因為window.count原來等于undefined,進行遞增運算后undefined++就等于
NaN.
為了解決這個問題,可以使用bind方法今魔,將inc內部的this綁定到counter對象
var func = counter.inc.bind(couonter)
func()
coounter.count // 1
問題2: 以下代碼輸出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
輸出一個彈框勺像,‘John:hi’
當把func函數賦值給john.sayHi時,相當于給對象john添加了一個sayHi的
方法错森,當john調用這個方法時咏删,這個方法內部的this會指向john
問題3: 下面代碼輸出什么,為什么
func()
function func() {
alert(this)
}
輸出window對象问词,因為func調用時處在全局環(huán)境中,它內部的this指向全局對象window
問題4:下面代碼輸出什么
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
分別輸出document和window
綁定事件的函數中嘀粱,this指向事件源
setTimeout中的this指向全局對象window
問題5:下面代碼輸出什么激挪,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
輸出John,因為通過call方法辰狡,把函數func中的this指向對象john,所以會輸出John
問題6: 以下代碼有什么問題垄分,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
不應該在事件綁定的函數中使用this指向Module宛篇,因為有事件綁定的回調
函數,它里面的this指向事件源薄湿,所以console.log(this)中的this指向事件源
$btn叫倍,this.showMsg()中的this也是指向$btn.
修改方法:
var module= {
bind: function(){
var _this = this//在this可以指向module的函數中先保存this
$btn.on('click', function(){
console.log(this) //this指什么
_this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
原型鏈相關問題
問題7:有如下代碼,解釋Person豺瘤、 prototype吆倦、proto、p坐求、constructor之間的關聯(lián)蚕泽。
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();
問題8: 上例中,對對象 p可以這樣調用 p.toString()桥嗤。toString是哪里來的? 畫出原型圖?并解釋什么是原型鏈须妻。
當對對象p調用toString方法時,首先他會在自己本身中查找有沒有這個方法泛领,如果沒有荒吏,則沿著他的p.ptoto這個指針去查找他的構造函數的原型對象中有沒有,即Person.prototype有沒有渊鞋,沒有的話繼續(xù)順著Person.prototype.proto這個指針繼續(xù)向上查找绰更,也就是說只要存在proto這個指針,在對應的屬性和方法沒有查到之前篓像,查找不會停下动知,直到沒有proto為止,也就是到達null為止员辩。我們把這個由proto指針串起來的直到Object.prototype.proto為 null的鏈叫做原型鏈盒粮。
問題9:對String做擴展,實現(xiàn)如下方式獲取字符串中頻率最高的字符
String.prototype.getMostOften = function(){
var res,
count = {},
times = 0
this.split('').forEach(function(item){
if(count.hasOwnProperty(item)){
count[item]++
}else {
count[item] = 1
}
})
for(key in count){
if(count[key]>times){
times = count[key]
res = key
}
}
return res + '因為' + res + '出現(xiàn)了' + times + '次'
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現(xiàn)了5次
問題10: instanceof有什么作用奠滑?內部邏輯是如何實現(xiàn)的丹皱?
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.
instanceof運算符用來判斷一個對象的原型鏈中是否存在某個構造函數的原型對象
function _instanceof(obj,func){
if(obj.__proto__ === func.prototype){//先判斷該對象的直接原型
對象是否等于構造函數的prototype屬性,如果是宋税,返回true
return true
}else {//如果不是摊崭,把obj的原型鏈上升一層,繼續(xù)判斷它的原型
對象的原型對象是否等于某個構造函數的prototype屬性杰赛,進行一個遞歸的判斷
return _instanceof(obj.__proto__,func)
}
}
繼承相關問題
問題11:繼承有什么作用?
首先什么是繼承:繼承是指一個對象可以直接使用另一個對象的屬性和方法
繼承提高了代碼的可重用性,因為子類擁有了父類的屬性和方法,修改代碼時只需修改父類的屬性和方法根时,那么子類的也會隨之修改
說到繼承瘦赫,不得不提多態(tài)胸遇,多態(tài)是指針對同一個方法荧呐,子類之間可以有不同的表現(xiàn),也就是說子類可以重寫或者覆蓋父類的方法纸镊,但又不影響父類本身倍阐,也可以對子類本身原型對象進行一些屬性或方法的補充和擴展。
問題12: 下面兩種寫法有什么區(qū)別?
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('饑人谷', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('若愚', 27);
第一種寫法把構造函數的所有屬性和方法都寫到了它本身逗威,那么每次把該構造函數實例化為對象的時候峰搪,都會把所有的屬性和方法都執(zhí)行一遍,內存中存儲了很多相同的方法
第二種寫法把構造函數的方法寫到了它的prototype屬性里凯旭,每次把該構造函數實例化的時候概耻,方法存在了其原型對象中,相當于創(chuàng)建了一份公共代碼罐呼,節(jié)約了內存鞠柄,提高了性能
問題13: Object.create 有什么作用?兼容性如何嫉柴?
Object.create方法用于從原型對象生成新的實例對象厌杜,可以替代new
命令
它接受一個對象作為參數,返回一個新對象计螺,后者完全繼承前者的屬
性夯尽,即原有對象成為新對象的原型。
var A = {
print: function(){
console.log('hello')
}
}
var B = Object.create(A)
B.print()//hello
B.print === A.print // true
上面代碼中登馒,object.create方法在A的基礎上生成了B匙握。此時,A就成
了B的原型陈轿,B就繼承了A的所有屬性和方法圈纺。這段代碼等同于下面的代碼
var A = function(){}
A.prototype.print = function(){
console.log('hello')
}
var B = new A()
B.print === A.prototype.print
實際上秦忿,Object.create方法可以用下面的代碼代替。如果老師瀏覽器
不支持Object.create方法赠堵,就可以用這段代碼自己部署
問題14: hasOwnProperty有什么作用小渊? 如何使用?
The hasOwnProperty() method returns a boolean indicating whether the object has the specified property as own (not inherited) property.
hasOwnProperty方法返回一個布爾值茫叭,判斷一個對象是否包含自定義屬性和方法,而不是原型鏈上的屬性和方法
function C(name,age){
this.name = name
this.age = age
}
C.prototype.sayName = function(){
console.log(this.name)
}
var d = new C('jack',10)
d.hasOwnProperty('name') // true
d.hasOwnProperty('sayName')//false
問題15:如下代碼中call的作用是什么?
function Person(name, sex){
this.name = name;
this.sex = sex;
}
function Male(name, sex, age){
Person.call(this, name, sex); //這里的call函數把Person中的this指向Male半等,并傳入參數name和sex
this.age = age;
}
問題16: 補全代碼揍愁,實現(xiàn)繼承
function Person(name, sex){
this.name = name
this.sex = sex
}
Person.prototype.getName = function(){
console.log(this.name)
};
function Male(name, sex, age){
Person.call(this,name,sex)//引用Person方法,把Person中的this指向Male杀饵,然后賦參數name和sex
this.age = age
}
Male.prototype = Object.create(Person.prototype)//以Person.prototype屬性為原型莽囤,創(chuàng)建一個新的對象,添加到Male.prototype
屬性里切距,相當于創(chuàng)建一個空對象朽缎,這個空對象的__proto__指向Person.prototype
Male.prototype.constructor = Male//將Male的prototype屬性中的constructor改為他自己
Male.prototype.getAge = function(){
console.log(this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.getName();
ruoyu.getAge();