1:apply、call 、bind有什么作用,什么區別
call 和 apply 都是為了改變某個函數運行時的上下文(context)而存在的,換句話說,就是為了改變函數體內部 this 的指向。
apply、call 的區別
對于 apply、call 二者而言,作用完全一樣,只是接受參數的方式不太一樣。例如,有一個函數定義如下:
var func = function(arg1, arg2) {
};
就可以通過如下方式來調用:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中 this 是你想指定的上下文,他可以是任何一個 JavaScript 對象(JavaScript 中一切皆對象),call 需要把參數按順序傳遞進去,而 apply 則是把參數放在數組里。
JavaScript 中,某個函數的參數數量是不固定的,因此要說適用條件的話,當你的參數是明確知道數量時用 call 。而不確定的時候用 apply,然后把參數 push 進數組傳遞進去apply 和 call 。
再來說說bind,bind() 方法與 apply 和 call 很相似,也是可以改變函數體內 this 的指向。
而bind,MDN的解釋是:bind()方法會創建一個新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作為 this,傳入 bind() 方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。
apply、call、bind比較
那么 apply、call、bind 三者相比較,之間又有什么異同呢?何時使用 apply、call,何時使用 bind 呢。簡單的一個栗子:
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
三個輸出的都是81,但是注意看使用 bind() 方法的,他后面多了對括號。
區別是,當你希望改變上下文環境之后并非立即執行,而是回調執行的時候,使用 bind() 方法。而 apply/call 則會立即執行函數。
總結一下:
- apply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
- apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文;
- apply 、 call 、bind 三者都可以利用后續參數傳參;
bind 是返回對應函數,便于稍后調用;apply 、call 則是立即調用 。
2: 以下代碼輸出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
輸出:John:hi!
誰在調用函數,誰就是this,所以this是john,代碼執行后彈出John:hi!
3: 下面代碼輸出什么,為什么
func()
function func() {
alert(this)
}
輸出 window
在函數被直接調用時this綁定到全局對象。在瀏覽器中,window 就是該全局對象
4:下面代碼輸出什么
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
輸出 document,window
綁定事件函數時,this表示觸發此事件的DOM對象。
setTimeout、setInterval這兩個方法執行的函數this也是全局對象
問題5:下面代碼輸出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
輸出 John ,call方法可以在函數調用的時候將第一個參數設置為this值,所以現在的this就是傳入的參數對象john。
6: 以下代碼有什么問題,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('加油');
}
}
這里的this指的是觸發此事件的DOM對象,不是module。
this.showMsg(); //由于this是$btn,所以在執行這句是會報錯。
var module= {
bind: function(){
var _this=this;
$btn.on('click', function(){
console.log(_this) //this指什么
_this.showMsg();
})
},
showMsg: function(){
console.log('加油');
}
}
7:有如下代碼,解釋Person、 prototype、proto、p、constructor之間的關聯。
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("wang")
p.sayName();
Person是構造函數,也是一個對象,這個對象里面存在一個prototype屬性,而構造函數內部定義了實例的屬性和方法,這些屬性和方法是屬于該類的所有實例的特征;
p是通過構造函數Person構造出來的實例,也是擁有proto屬性。
p.__proto__ === Person.prototype;
prototype是構造函數內部的原型對象,所以擁有contructor和proto屬性,其中contructor屬性指向構造函數Person,proto指向該對象的原型,即
Person.prototype.__proto__ === Object.prototype;
Person.prototype.constructor == Person
8: 上例中,對對象 p可以這樣調用 p.toString()。toString是哪里來的? 畫出原型圖?并解釋什么是原型鏈。
如圖所示,p.toString()方法是繼承構造函數Object的原型對象里定義的toString方法,首先p會找自己的toString方法,如果沒有找到,會沿著proto屬性繼續到構造函數Person的prototype里找toString方法,如果還未找到,再繼續往Person.prototype的proto即Object.prototype找toString方法,最后找到toString()方法。
原型鏈:由于原型對象本身也是對象,而每個javascript對象都有一個原型對象,每個對象都有一個隱藏的proto屬性,原型對象也有自己的原型,而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個就是原型鏈。在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找,如果找到,直接返回值,如果整個鏈都遍歷且沒有找到屬性,則返回undefined。原型鏈一般實現為一個鏈表,這樣就可以按照一定的順序來查找。
9:對String做擴展,實現如下方式獲取字符串中頻率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現了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("出現頻率最多的字符為"+maxValue+",共有"+count+"個");
}
str=new String(str);
var ch = str.getMostOften();
console.log(ch);
10: instanceOf有什么作用?內部邏輯是如何實現的?
instanceOf:判斷一個對象是否為另一個對象的實例
instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
語法
object instanceof constructor
參數
object
要檢測的對象.
constructor
某個構造函數
內部邏輯
function instanceOf(obj,fn) {
var oldProto = obj.__proto__;
do {
if (oldProto=== fn.prototype){
return true;
}else {
oldProto = oldProto.__proto__
}
}while(oldProto){
return false;
}
11:繼承有什么作用?
作用:
- 子類擁有父類的屬性和方法,不需要重復寫代碼,修改時也只需修改一份代碼
- 可以重寫和擴展父類的屬性和代碼,又不影響父類本身
12: 下面兩種寫法有什么區別?
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('wang', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('hu', 27);
- 第一種方式是將所有的屬性和方法都寫在構造函數中,那么創建的每一個實例都會存有相同的方法,會造成很大的內存浪費。
- 而第二種是將方法定義在原型上,這樣只有在原型對象上才會有這個公有方法,所有實例對象都可以共享。
13: Object.create 有什么作用?兼容性如何?
Object.create() 方法使用指定的原型對象和其屬性創建了一個新的對象。
語法
Object.create(proto, [ propertiesObject ])
14: hasOwnProperty有什么作用? 如何使用?
obj,hasOwnProperty(pro) 判斷某個對象是否含有指定的屬性但是該方法會忽略掉那些從原型鏈上繼承到的屬性
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調用Person方法,指定Person方法中的this為Male,并傳入參數sex,age
16: 補全代碼,實現繼承
unction 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);
Male.prototype.constructor = Male;
Male.prototype.getAge = function(){
console.log("age:"+this.age);
};
Male.prototype.printName = function(){
this.getName();
this.getAge();
}
var ruoyu = new Male('王虎', '男', 26);
ruoyu.printName();