1.以下代碼輸出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()//John:hi!
輸出John:hi!。解析:john.sayHi()可理解為john.sayHi.call(john),即this指向john,輸出即為John.firstName + ":hi!"。
2.以下代碼輸出什么?
func()
function func() {
alert(this)
}
輸出window對象。func()相當(dāng)于func.call(undefined),故輸出window對象。若要改變this指向,則應(yīng)寫成:func.call(obj)
3.下面代碼輸出什么?
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
單擊頁面,依次輸出document對象、window對象。
DOM對象綁定事件時,傳入的函數(shù)中的this綁定到該DOM對象
setTImeout 和 setInterval 這兩個方法執(zhí)行的函數(shù)中的this綁定到全局對象window。
4.以下代碼有什么問題,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指$btn
this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
在事件監(jiān)聽函數(shù)中 this.showMsg(), this指代 $btn 這個jQuery對象,并不代表module對象,因此 this.showMsg() 相當(dāng)于 $btn.showMsg()。
想要在事件監(jiān)聽函數(shù)中 調(diào)用module 的方法,可以人為把this綁定到module對象,使用 bind() 即可:
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指向module
this.showMsg();
}.bind(this))
},
showMsg: function(){
console.log('饑人谷');
}
}
也可以將module的this保存起來為that,最后用that.showMsg()去執(zhí)行。
也可以用ES6的箭頭函數(shù)。
5.有如下代碼,解釋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("Li")
p.sayName();
- 首先,
Person是一個構(gòu)造函數(shù),而p是它的一個實例。 - 故,
p.__proto__ == Person.prototype。 -
prototype對象有一個constructor屬性,指向prototype對象所在的構(gòu)造函數(shù)。故Person.prototype.constructor指向Person,p.__proto__.constructor也指向Person。
6.上例中,對對象 p可以這樣調(diào)用 p.toString()。toString是哪里來的? 畫出原型圖?并解釋什么是原型鏈。

如圖所示,p自身沒有toString,就到Person類的prototype去找,也沒有,prototype是個對象,由Object類創(chuàng)建,但是仍然沒有toString,所以繼續(xù)到prototype的_ proto _ 也就是Object的prototype中去找,最終找到了toString方法。
由此引出原型鏈的概念:
每個對象都有一個proto屬性,其指向該對象的原型對象,而原型對象也是對象,所以也有一個proto指向原型對象的原型對象,這樣就組成了一條鏈,稱為原型鏈。在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找,如果找到,直接返回值。
原型鏈?zhǔn)菍崿F(xiàn)繼承的主要方法。
7.對String做擴展,實現(xiàn)如下方式獲取字符串中頻率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因為d 出現(xiàn)了5次
代碼如下:
String.prototype.getMostOften = function(){
var obj = {};
var max = 0;
var result;
for(var i=0; i<this.length; i++){
if(obj[str[i]]){
obj[str[i]] += 1;
} else {
obj[str[i]] = 1;
}
}
for(var key in obj){
if(obj[key]>max){
max = obj[key];
result = key;
}
}
return result;
};
8.instanceOf有什么作用?內(nèi)部邏輯是如何實現(xiàn)的?
instanceOf運算符可以判斷一個對象是否是某個類的實例。
如果一個對象通過沿著 _ proto _ 組成的原形鏈一層層查找其constructor屬性,如果有一層proto等于某類的prototype,則該對象是某類型的實例。
function instance(obj,fun){
if(obj.__proto__){
if(obj.__proto__ === fun.prototype){
return true;
} else {
return instance(obj.__proto__,fun);
}
}
return false;
}
console.log(instance([],Array)); //true
console.log(instance([],Object)); //true
console.log(instance([],String)); //false
9.繼承有什么用?
繼承是指一個對象直接使用另一對象的屬性和方法。
繼承機制使得不同的實例可以共享構(gòu)造函數(shù)的原型對象的屬性和方法,提高了代碼的復(fù)用性。
10. 下面兩種寫法有什么區(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);
同樣是綁定一個printName方法,前者直接將方法寫在構(gòu)造函數(shù)上,而后者將方法寫在構(gòu)造函數(shù)的prototype對象上,前者在每生成一個實例之后實例的printName就占用內(nèi)存,而后者每生成一個實例后會共享構(gòu)造函數(shù)prototype對象上的printName方法,以達(dá)到節(jié)省內(nèi)存的效果,也便于后期維護。
11.Object.create 有什么作用?兼容性如何?
- 該方法接受一個對象作為參數(shù),然后以它為原型,返回一個實例對象。該實例完全繼承原型對象的屬性。還可傳入第二個參數(shù),該參數(shù)是一個屬性描述對象,它所描述的對象屬性,會添加到實例對象,作為該對象自身的屬性。
- Object.create是ES5中的方法,各大瀏覽器的最新版本(包括IE9)都部署了這個方法。如果老式瀏覽器不支持Object.create方法,可以就用這段代碼自己部署。
if(typeof Object.create !== 'function'){
Object.create = function(obj){
function F(){}
F.prototype = obj;
return new F();
};
}
以上代碼說明,Object.create方法的實質(zhì)是新建一個構(gòu)造函數(shù)F,使其prototype屬性指向參數(shù)對象obj,最后返回一個F的實例,從而讓該實例繼承obj對象的屬性。
12.hasOwnProperty有什么作用? 如何使用?
hasOwnProperty方法用于返回一個布爾值,用于判斷某個屬性定義在對象自身,還是定義在原型鏈上。
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('Li', 23)
p1.hasOwnProperty('name') //true
p1.hasOwnProperty('printName') //false
13.如下代碼中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函數(shù)執(zhí)行上下文中的this為當(dāng)前的Male創(chuàng)建的新對象。這樣通過Male構(gòu)造出的對象,便能繼承擁有Person中的屬性,也就是name 和sex。
14.補全代碼,實現(xiàn)繼承
function Person(name, sex){
// todo ...
}
Person.prototype.getName = function(){
// todo ...
};
function Male(name, sex, age){
//todo ...
}
//todo ...
Male.prototype.getAge = function(){
//todo ...
};
var li = new Male('Li', '男', 24);
li.printName();
代碼如下:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.printName = function(){
console.log(this.name);
};
function Male(name,age,sex){
this.sex = sex;
Person.call(this,name,age);
}
Male.prototype = Object.create(Person.prototype);
Male.prototype.constructor = Male;
Male.prototype.printSex = function(){
console.log(this.sex);
};
var a = new Male('Li',24,'male');
a.printName(); //Li
也可封裝成一個函數(shù)使用:
function inherit(superType,subType){
var _superType = Object.create(superType.prototype);
_superType.constructor = superType;
subType.prototype = _superType;
}
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.printName = function(){
console.log(this.name);
};
function Male(name,age,sex){
this.sex = sex;
Person.call(this,name,age);
}
inherit(Person,Male);
Male.prototype.printSex = function(){
console.log(this.sex);
};
var a = new Male('Li',24,'male');
a.printName(); //Li