1: apply、call 、bind有什么作用,什么區(qū)別
call 和 apply 都是為了改變某個(gè)函數(shù)運(yùn)行時(shí)的上下文(context)而存在的,換句話說(shuō),就是為了改變函數(shù)體內(nèi)部 this 的指向。
apply、call 的區(qū)別
對(duì)于 apply、call 二者而言,作用完全一樣,只是接受參數(shù)的方式不太一樣。例如,有一個(gè)函數(shù)定義如下:
var func = function(arg1, arg2) {
};
就可以通過(guò)如下方式來(lái)調(diào)用:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中 this 是你想指定的上下文,他可以是任何一個(gè) JavaScript 對(duì)象(JavaScript 中一切皆對(duì)象),call 需要把參數(shù)按順序傳遞進(jìn)去,而 apply 則是把參數(shù)放在數(shù)組里。
JavaScript 中,某個(gè)函數(shù)的參數(shù)數(shù)量是不固定的,因此要說(shuō)適用條件的話,當(dāng)你的參數(shù)是明確知道數(shù)量時(shí)用 call 。而不確定的時(shí)候用 apply,然后把參數(shù) push 進(jìn)數(shù)組傳遞進(jìn)去
apply 和 call ,再來(lái)說(shuō)說(shuō)bind。bind() 方法與 apply 和 call 很相似,也是可以改變函數(shù)體內(nèi) this 的指向。
而bind,MDN的解釋是:bind()方法會(huì)創(chuàng)建一個(gè)新函數(shù),稱為綁定函數(shù),當(dāng)調(diào)用這個(gè)綁定函數(shù)時(shí),綁定函數(shù)會(huì)以創(chuàng)建它時(shí)傳入 bind()方法的第一個(gè)參數(shù)作為 this,傳入 bind() 方法的第二個(gè)以及以后的參數(shù)加上綁定函數(shù)運(yùn)行時(shí)本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來(lái)調(diào)用原函數(shù)。
apply、call、bind比較
那么 apply、call、bind 三者相比較,之間又有什么異同呢?何時(shí)使用 apply、call,何時(shí)使用 bind 呢。簡(jiǎn)單的一個(gè)栗子:
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
三個(gè)輸出的都是81,但是注意看使用 bind() 方法的,他后面多了對(duì)括號(hào)。
區(qū)別是,當(dāng)你希望改變上下文環(huán)境之后并非立即執(zhí)行,而是回調(diào)執(zhí)行的時(shí)候,使用 bind() 方法。而 apply/call 則會(huì)立即執(zhí)行函數(shù)。
總結(jié)一下:
apply 、 call 、bind 三者都是用來(lái)改變函數(shù)的this對(duì)象的指向的;
apply 、 call 、bind 三者第一個(gè)參數(shù)都是this要指向的對(duì)象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后續(xù)參數(shù)傳參;
bind 是返回對(duì)應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。
this 相關(guān)問(wèn)題
2: 以下代碼輸出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
輸出:John:hi!
誰(shuí)在調(diào)用函數(shù),誰(shuí)就是this,所以this是john,代碼執(zhí)行后彈出John:hi!
3: 下面代碼輸出什么,為什么
func()
function func() {
alert(this)
}
輸出 window
在函數(shù)被直接調(diào)用時(shí)this綁定到全局對(duì)象。在瀏覽器中,window 就是該全局對(duì)象
4:下面代碼輸出什么
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
輸出 document,window
綁定事件函數(shù)時(shí),this表示觸發(fā)此事件的DOM對(duì)象。
setTimeout、setInterval這兩個(gè)方法執(zhí)行的函數(shù)this也是全局對(duì)象
5:下面代碼輸出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
輸出 John ,call方法可以在函數(shù)調(diào)用的時(shí)候?qū)⒌谝粋€(gè)參數(shù)設(shè)置為this值,所以現(xiàn)在的this就是傳入的參數(shù)對(duì)象john。
6: 以下代碼有什么問(wèn)題,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
這里的this指的是觸發(fā)此事件的DOM對(duì)象,不是module。
this.showMsg(); //由于this是$btn,所以在執(zhí)行這句是會(huì)報(bào)錯(cuò)。
var module= {
var _this=this;
bind: function(){
$btn.on('click', function(){
console.log(_this) //this指什么
_this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
原型鏈相關(guān)問(wèn)題
7:有如下代碼,解釋Person、 prototype、proto、p、constructor之間的關(guān)聯(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();
···
Person是構(gòu)造函數(shù),也是一個(gè)對(duì)象,這個(gè)對(duì)象里面存在一個(gè)prototype屬性,而構(gòu)造函數(shù)內(nèi)部定義了實(shí)例的屬性和方法,這些屬性和方法是屬于該類的所有實(shí)例的特征;
p是通過(guò)構(gòu)造函數(shù)Person構(gòu)造出來(lái)的實(shí)例,也是擁有proto屬性。所以
p.__proto__ === Person.prototype;
prototype是構(gòu)造函數(shù)內(nèi)部的原型對(duì)象,所以擁有contructor和proto屬性,其中contructor屬性指向構(gòu)造函數(shù)Person,proto指向該對(duì)象的原型,即
Person.prototype.__proto__ === Object.prototype;
Person.prototype.constructor == Person
8: 上例中,對(duì)對(duì)象 p可以這樣調(diào)用 p.toString()。toString是哪里來(lái)的? 畫出原型圖?并解釋什么是原型鏈。

如圖所示,p.toString()方法是繼承構(gòu)造函數(shù)Object的原型對(duì)象里定義的toString方法,首先p會(huì)找自己的toString方法,如果沒(méi)有找到,會(huì)沿著proto屬性繼續(xù)到構(gòu)造函數(shù)Person的prototype里找toString方法,如果還未找到,再繼續(xù)往Person.prototype的proto即Object.prototype找toString方法,最后找到toString()方法。
原型鏈:由于原型對(duì)象本身也是對(duì)象,而每個(gè)javascript對(duì)象都有一個(gè)原型對(duì)象,每個(gè)對(duì)象都有一個(gè)隱藏的proto屬性,原型對(duì)象也有自己的原型,而它自己的原型對(duì)象又可以有自己的原型,這樣就組成了一條鏈,這個(gè)就是原型鏈。在訪問(wèn)對(duì)象的屬性時(shí),如果在對(duì)象本身中沒(méi)有找到,則會(huì)去原型鏈中查找,如果找到,直接返回值,如果整個(gè)鏈都遍歷且沒(méi)有找到屬性,則返回undefined。原型鏈一般實(shí)現(xiàn)為一個(gè)鏈表,這樣就可以按照一定的順序來(lái)查找。
9:對(duì)String做擴(kuò)展,實(shí)現(xiàn)如下方式獲取字符串中頻率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因?yàn)閐 出現(xiàn)了5次
var str = 'ahbbccdeddddfg';
function String(str){
this.str=str;
}
String.prototype.getMostOften=function(){
var obj={};
for(var i=0;i<this.str.length;i++){
if(obj[this.str[i]]){
obj[this.str[i]]++;
}else{
obj[this.str[i]]=1;
}
}
console.log(obj);
var count=0,maxValue;
for(var key in obj){
if(obj[key]>count){
maxValue=key;
count=obj[key];
}
}
console.log("出現(xiàn)頻率最多的字符為"+maxValue+",共有"+count+"個(gè)");
}
str=new String(str);
var ch = str.getMostOften();
console.log(ch);
10: instanceOf有什么作用??jī)?nèi)部邏輯是如何實(shí)現(xiàn)的?
instanceOf:判斷一個(gè)對(duì)象是否為另一個(gè)對(duì)象的實(shí)例
instanceof 運(yùn)算符用來(lái)測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性。
語(yǔ)法
object instanceof constructor
參數(shù)
object
要檢測(cè)的對(duì)象.
constructor
某個(gè)構(gòu)造函數(shù)
內(nèi)部邏輯
function instanceOf(obj,fn) {
var oldProto = obj.__proto__;
do {
if (oldProto=== fn.prototype){
return true;
}else {
oldProto = oldProto.__proto__
}
}while(oldProto)
return false;
}
繼承相關(guān)問(wèn)題
11:繼承有什么作用?
作用:
- 子類擁有父類的屬性和方法,不需要重復(fù)寫代碼,修改時(shí)也只需修改一份代碼
- 可以重寫和擴(kuò)展父類的屬性和代碼,又不影響父類本身
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);
第一種方式是將所有的屬性和方法都寫在構(gòu)造函數(shù)中,那么創(chuàng)建的每一個(gè)實(shí)例都會(huì)存有相同的方法,會(huì)造成很大的內(nèi)存浪費(fèi)。而第二種是將方法定義在原型上,這樣只有在原型對(duì)象上才會(huì)有這個(gè)公有方法,所有實(shí)例對(duì)象都可以共享。
13: Object.create 有什么作用?兼容性如何?
Object.create() 方法使用指定的原型對(duì)象和其屬性創(chuàng)建了一個(gè)新的對(duì)象。
語(yǔ)法
Object.create(proto, [ propertiesObject ])

14: hasOwnProperty有什么作用? 如何使用?
obj,hasOwnProperty(pro) 判斷某個(gè)對(duì)象是否含有指定的屬性但是該方法會(huì)忽略掉那些從原型鏈上繼承到的屬性
function People(){
this.name='lwk'
}
People.prototype.sayName=function(){console.log(this.name)}
var p = new People()
p.hasOwnProperty('name')//true
p.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 有什么作用
this.age = age;
}
call調(diào)用Person方法,指定Person方法中的this為Male,并傳入?yún)?shù)sex,age
16: 補(bǔ)全代碼,實(shí)現(xiàn)繼承
function Person(name, sex){
this.name=name;
this.sex=sex;
}
Person.prototype.getName = function(){
console.log("name:"+this.name);
};
function Male(name, sex, age){
Person.call(this,name,sex)
this.age=age;
}
Male.prototype=Object.create(Person.prototype,name);
Male.prototype.constructor=Male;
console.dir(Object.create(Person.prototype));
Male.prototype.getAge = function(){
console.log("age:"+this.age);
};
Male.prototype.printName=function(){
this.getName();
this.getAge();
}
var ruoyu = new Male('高進(jìn)', '男', 24);
ruoyu.printName();