问题1: apply、call 、bind有什么作用,什么区别
在实际编程过程中,this的动态切换很容易让人混淆它的指向,为了把this固定下来,避免意想不到的情况,javascript提供了apply,call,bind三种方法来切换/ 固定this的指向。
function.prototype.apply()
函数实例的call方法,可以指定该函数内部this的指向,即函数执行时所在的作用域,然后在所指定的作用域中,调用该函数
var obj = {}
function foo(){
return this
}
foo() === this //true
foo.call(obj) === obj //true
上面代码中,在全局环境运行函数foo时,this指向全局环境;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的指向,然后再调用该函数。唯一的区别是,它接收一个数组作为函数执行时
的参数,使用格式如下
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})//[],没有length属性,默认不被识别为类数组对象,返回空数组
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以后,在全部环境中调用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全局环境中并没有count这个变量
上面代码中,函数func是在全局环境中运行的,这时inc内部的this指向顶层对象window,所以counter.count是不会
变的,反而创建了一个全局变量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调用时处在全局环境中,它内部的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之间的关联。
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有没有,没有的话继续顺着Person.prototype.proto这个指针继续向上查找,也就是说只要存在proto这个指针,在对应的属性和方法没有查到之前,查找不会停下,直到没有proto为止,也就是到达null为止。我们把这个由proto指针串起来的直到Object.prototype.proto为 null的链叫做原型链。
问题9:对String做扩展,实现如下方式获取字符串中频率最高的字符
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 + '出现了' + times + '次'
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
问题10: instanceof有什么作用?内部逻辑是如何实现的?
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的原型链上升一层,继续判断它的原型
对象的原型对象是否等于某个构造函数的prototype属性,进行一个递归的判断
return _instanceof(obj.__proto__,func)
}
}
继承相关问题
问题11:继承有什么作用?
首先什么是继承:继承是指一个对象可以直接使用另一个对象的属性和方法
继承提高了代码的可重用性,因为子类拥有了父类的属性和方法,修改代码时只需修改父类的属性和方法,那么子类的也会随之修改
说到继承,不得不提多态,多态是指针对同一个方法,子类之间可以有不同的表现,也就是说子类可以重写或者覆盖父类的方法,但又不影响父类本身,也可以对子类本身原型对象进行一些属性或方法的补充和扩展。
问题12: 下面两种写法有什么区别?
//方法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);
第一种写法把构造函数的所有属性和方法都写到了它本身,那么每次把该构造函数实例化为对象的时候,都会把所有的属性和方法都执行一遍,内存中存储了很多相同的方法
第二种写法把构造函数的方法写到了它的prototype属性里,每次把该构造函数实例化的时候,方法存在了其原型对象中,相当于创建了一份公共代码,节约了内存,提高了性能
问题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: 补全代码,实现继承
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属性为原型,创建一个新的对象,添加到Male.prototype
属性里,相当于创建一个空对象,这个空对象的__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();