问题1: apply、call 、bind有什么作用,什么区别?
- apply和call
- call apply,调用一个函数,传入函数执行上下文及参数
fn.call(context, param1, param2...)
fn.apply(context, paramArray)
语法很简单,第一个参数都是希望设置的this对象,不同之处在于call方法接收参数列表,而apply接收参数数组
fn2.call(obj1);
fn2.apply(obj1);
- bind,返回一个新函数,并且使函数内部的this为传入的第一个参数
var fn3 = obj1.fn.bind(obj1);
fn3();
问题2: 以下代码输出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
//输出:"John: hi!"
this指向john
问题3:下面代码输出什么,为什么
func()
function func() {
alert(this)
}
//输出的是window,因为可以写成call调用的形式,func.call(context),
//因为context没有传,在非严格模式下this就是window对象 。
问题4:下面代码输出什么
document.addEventListener('click', function(e){
console.log(this);//输出结果为document,此时的this代表着document
setTimeout(function(){
console.log(this);////输出结果为window,setTimeout执行的函数this是全局变量,即window
}, 200);
}, false);
问题5:下面代码输出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
//输出"John",call中传入了john
问题6: 以下代码有什么问题,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指的是$btn
this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
----------------------------------------分割线---------------------------------------
var module= {
bind: function(){
var cur = this
$btn.on('click', function(){
console.log(this)
cur.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();
即p是person的一个实例,p的proto指向person的prototype,person的prototype的proto指向对象的prototype,这里到达顶级,另外constructor即构造函数person本身。
问题8: 上例中,对对象 p可以这样调用 p.toString()。toString是哪里来的? 画出原型图?并解释什么是原型链。
-
toString
- 通过函数定义了person,类(函数)自动获得属性prototype
- 每个类的实例都会有一个内部属性
_proto_
,指向类的prototype
问题9:对String做扩展,实现如下方式获取字符串中频率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
String.prototype.getMostOften = function(){
var obj = {};
for( var i = 0; i < this.length; i++ ){
var val = this[i];
if( obj[val] ){
obj[val]++;
}else{
obj[val] = 1;
}
}
var max = 0;
var val;
for( var key in obj){
if( obj[key] > max ){
max = obj[key];
val = key;
}
}
return val;
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch);
问题10: instanceOf有什么作用?内部逻辑是如何实现的?
- instanceOf运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype属性。
即instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
// 定义构造函数
function C(){}
function D(){}
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof C;
// false,因为 D.prototype不在o的原型链上
o instanceof D;
o instanceof Object; // true,因为Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype指向了一个空对象,这个空对象不在o的原型链上.
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true
需要注意的是,如果表达式 obj instanceof Foo 返回true,则并不意味着该表达式会永远返回true,因为Foo.prototype属性的值有可能会改变,改变之后的值很有可能不存在于obj的原型链上,这时原表达式的值就会成为false。另外一种情况下,原表达式的值也会改变,就是改变对象obj的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的proto魔法属性,是可以实现的。比如执行obj.proto = {}之后,obj instanceof Foo就会返回false了。
- instanceof和多全局对象(多个frame或多个window之间的交互)
在浏览器中,我们的脚本可能需要在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。比如,表达式 [] instanceof window.frames[0].Array 会返回false,因为 Array.prototype !== window.frames[0].Array.prototype,因此你必须使用 Array.isArray(myObj) 或者 Object.prototype.toString.call(myObj) === "[object Array]"来判断myObj是否是数组。
继承相关问题
问题11:继承有什么作用?
- 继承是指一个对象直接使用另一对象的属性和方法。
JavaScript并不提供原生的继承机制,我们自己实现的方式很多,介绍一种最为通用的,通过上面定义我们可以看出我们如果实现了两点的话就可以说我们实现了继承 - 得到一个类的属性
- 得到一个类的方法
我们分开讨论一下,先定义两个类
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
};
function Male(age){
this.age = age;
}
Male.prototype.printAge = function(){
console.log(this.age);
};
实例化看看结果
var m = new Male('Byron', 'male', 26);
console.log(m.sex); // "male"
- 方法获取
我们知道类的方法都定义在了prototype里面,所以只要我们把子类的prototype改为父类的prototype的备份就好了
Male.prototype = Object.create(Person.prototype);
这里我们通过Object.createclone了一个新的prototype而不是直接把Person.prtotype直接赋值,因为引用关系,这样会导致后续修改子类的prototype也修改了父类的prototype,因为修改的是一个值
另外Object.create是ES5方法,之前版本通过遍历属性也可以实现浅拷贝
这样做需要注意一点就是对子类添加方法,必须在修改其prototype之后,如果在之前会被覆盖掉
Male.prototype.printAge = function(){
console.log(this.age);
};
Male.prototype = Object.create(Person.prototype);
这样的话,printAge方法在赋值后就没了,因此得这么写
function Male(name, sex, age){
Person.call(this, name, sex);
this.age = age;
}
Male.prototype = Object.create(Person.prototype);
Male.prototype.printAge = function(){
console.log(this.age);
};
这样写貌似没问题了,但是有个问题就是我们知道prototype对象有一个属性constructor指向其类型,因为我们复制的父元素的prototype,这时候constructor属性指向是不对的,导致我们判断类型出错
Male.prototype.constructor; //Person
因此我们需要再重新指定一下constructor属性到自己的类型
- 最终方案
我们可以通过一个函数实现刚才说的内容
function inherit(superType, subType){
var _prototype = Object.create(superType.prototype);
_prototype.constructor = subType;
subType.prototype = _prototype;
}
- 使用方式
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
};
function Male(name, sex, age){
Person.call(this, name, sex);
this.age = age;
}
inherit(Person, Male);
// 在继承函数之后写自己的方法,否则会被覆盖
Male.prototype.printAge = function(){
console.log(this.age);
};
var m = new Male('Byron', 'm', 26);
m.printName();
这样我们就在JavaScript中实现了继承
问题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);
方法2将方法写在原型链上,起到了公共代码的作用,节约了代码量,提高了性能
问题13: Object.create 有什么作用?兼容性如何?
- Object.create() 方法使用指定的原型对象和其属性创建了一个新的对象。
Object.create(proto, [ propertiesObject ])
- 兼容性
**问题14: **hasOwnProperty有什么作用? 如何使用?
- hasOwnProperty() 方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。
obj.hasOwnProperty(prop)
所有继承了 Object
的对象都会继承到 hasOwnProperty
方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in
运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
- 使用
- 使用 hasOwnProperty 方法判断属性是否存在
下面的例子检测了对象 o 是否含有自身属性 prop:
o = new Object();
o.prop = 'exists';
function changeO() {
o.newprop = o.prop;
delete o.prop;
}
o.hasOwnProperty('prop'); // 返回 true
changeO();
o.hasOwnProperty('prop'); // 返回 false
- 自身属性与继承属性
下面的例子演示了 hasOwnProperty 方法对待自身属性和继承属性的区别:
o = new Object();
o.prop = 'exists';
o.hasOwnProperty('prop'); // 返回 true
o.hasOwnProperty('toString'); // 返回 false
o.hasOwnProperty('hasOwnProperty'); // 返回 false
- 遍历一个对象的所有自身属性
下面的例子演示了如何在遍历一个对象的所有属性时忽略掉继承属性,注意这里 for...in
循环只会遍历可枚举属性,所以不应该基于这个循环中没有不可枚举的属性而得出 hasOwnProperty 是严格限制于可枚举项目的(如同
Object.getOwnPropertyNames()
)。
var buz = { fog: 'stack'};for (var name in buz)
{ if (buz.hasOwnProperty(name))
{ alert("this is fog (" + name + ") for sure. Value: " + buz[name]); }
else { alert(name); // toString or something else }}
- 使用 hasOwnProperty 作为属性名
JavaScript 并没有保护 hasOwnProperty 属性名,因此某个对象是有可能存在使用这个属性名的属性,使用外部的 hasOwnProperty 获得正确的结果是需要的:
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 始终返回 false
// 如果担心这种情况,可以直接使用原型链上真正的 hasOwnProperty 方法
({}).hasOwnProperty.call(foo, 'bar'); // true
// 也可以使用 Object 原型上的 hasOwnProperty 属性
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
问题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: 补全代码,实现继承
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);
this.age = age;
}
inherit(Person, Male);
Male.prototype.getAge = function(){
console.log(this.age);
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();