一、this 相关问题
知乎上关于this的解答 this 的值到底是什么?一次说清楚
this 的工作原理在js秘密花园中是这么解释的
JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 在五种不同的情况下 ,this 指向的各不相同。
全局范围内
this;
当在全部范围内使用this
,它将会指向全局对象。
函数调用
foo();
这里 this也会指向全局对象。
ES5 注意: 在严格模式下(strict mode),不存在全局变量。 这种情况下 this将会是 undefined
方法调用
test.foo();
这个例子中,this
指向 test
对象。
调用构造函数
new foo();
如果函数倾向于和 new关键词一块使用,则我们称这个函数是 构造函数。 在函数内部,this指向新创建的对象。
显式的设置 this
function foo(a, b, c) {
}
var bar = {};
foo.apply(bar, [1, 2, 3]); // 数组将会被扩展,如下所示
foo.call(bar, 1, 2, 3); // 传递到foo的参数是:a = 1, b = 2, c = 3
当使用 Function.prototype
上的 call
或者 apply
方法时,函数内的 this
将会被 显式设置为函数调用的第一个参数。因此函数调用的规则在上例中已经不适用了,在foo
函数内this
被设置成了bar
。
注意: 在对象的字面声明语法中,this 不能用来指向对象本身。 因此 var obj = {me: this} 中的 me
不会指向 obj,因为 this 只可能出现在上述的五种情况中。 译者注:这个例子中,如果是在浏览器中运行,obj.me等于 window 对象。
常见误解
尽管大部分的情况都说的过去,不过第一个规则(译者注:这里指的应该是第二个规则,也就是直接调用函数时,this
指向全局对象) 被认为是JavaScript语言另一个错误设计的地方,因为它从来就没有实际的用途。
Foo.method = function() {
function test() {
// this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
}
test();
}
一个常见的误解是 test
中的this
将会指向Foo
对象,实际上不是这样子的。为了在 test 中获取对 Foo对象的引用,我们需要在 method
函数内部创建一个局部变量指向Foo
对象。
Foo.method = function() {
var that = this;
function test() {
// 使用 that 来指向 Foo 对象
}
test();
}
that
只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 this对象。
方法的赋值表达式
另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。
var test = someObject.methodTest;
test();
上例中,test
就像一个普通的函数被调用;因此,函数内的this
将不再被指向到someObject
对象。
虽然 this的晚绑定特性似乎并不友好,但是这确实基于原型继承赖以生存的土壤。
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
当 method被调用时,this将会指向 Bar的实例对象。
1. apply、call 有什么作用,什么区别
相同之处:
call与apply的作用,都是调用一个函数,传入函数执行上下文及参数,第一个参数都是希望设置的this对象。
不同之处:
不同之处在于call方法接收若干个参数的列表,而apply接收一个包含多个参数的参数数组
Javascript的每个Function对象中有一个apply方法:
function.apply([thisObj[,argArray]])
还有一个类似功能的call方法:
function.call([thisObj[,arg1[, arg2[, [,.argN]]]]])
它们各自的定义:
apply
:应用某一对象的一个方法,用另一个对象替换当前对象。
call
:调用一个对象的一个方法,以另一个对象替换当前对象。
它们的共同之处:
都“可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
它们的不同之处:
apply
:
最多只能有两个参数——新this对象和一个数组 argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里面。如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj,并且无法被传递任何参数。
call
:
则是直接的参数列表,主要用在js对象各方法互相调用的时候,使当前this实例指针保持一致,或在特殊情况下需要改变this指针。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
更简单地说,apply和call功能一样,只是传入的参数列表形式不同:如 func.call(func1,var1,var2,var3) 对应的apply写法为:func.apply(func1,[var1,var2,var3])
也就是说:call调用的为单个,apply调用的参数为数组
function sum(a,b){ console.log(this === window);//true console.log(a + b); } sum(1,2); sum.call(null,1,2); sum.apply(null,[1,2]);
作用
调用函数
var info = 'tom';
function foo(){
//this指向window
var info = 'jerry';
console.log(this.info); //tom
console.log(this===window) //true
}
foo();
foo.call();
foo.apply();
call和apply可以改变函数中this的指向
var obj = {
info:'spike'
}
foo.call(obj); //这里foo函数里面的this就指向了obj
foo.apply(obj)
借用别的对象的方法,求数组中的最大值
var arr = [123,34,5,23,3434,23];
方法一
var arr1 = arr.sort(function(a,b){
return b-a;
});
console.log(arr1[0]);
方法二
var max = Math.max.apply(null,arr) //借用别的对象的方法
console.log(max);
fn.call(context, param1, param2...)
fn.apply(context, paramArray)
2. 以下代码输出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
输出结果为'John: hi!'
。这是由于john.sayHi = func
语句为对象john
新建了一个属性,该属性值为函数func
,当函数被调用时,此时的this
为对象john
本身。
3. 下面代码输出什么,为什么
func()
function func() {
alert(this)
}
输出为window
对象。由于func
是在全局作用域下被调用。
4. 下面代码输出什么
function fn0(){
function fn(){
console.log(this);
}
fn();
}
fn0();
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
第一个输出window
,此时是全局变量调用的fn0
,第二个是document
,是DOM对象本身,第三个是window
,这是由于setTimeout
函数是挂在window
对象下的。
5. 下面代码输出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
输出'John'
,这是由于.call()
方法为函数指定了执行环境,在对象john
下。
6. 代码输出?
var john = {
firstName: "John",
surname: "Smith"
}
function func(a, b) {
alert( this[a] + ' ' + this[b] )
}
func.call(john, 'firstName', 'surname')
输出'John Smith'
,这是由于func.call(john, 'firstName', 'surname')
为函数指定了执行环境为john
对象,传入参数分别为'firstName'、'surname'
,也就是取john
的这两个属性值,将其相加,得出结果。
7. 以下代码有什么问题,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
bind属性中,this.showMsg()语句的this指的是$btn,是无法调用showMsg()的,此时需要保存事件绑定函数外部的this,修改如下:
var module= {
bind: function(){
var cur = this
$btn.on('click', function(){
console.log(this) //this指什么
cur.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
二、原型链相关问题
知乎上关于this的解答什么是 JS 原型链?
1. 有如下代码,解释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();
person
是构造函数;
prototype
是构造函数的原型对象;
p
是构造函数的实例;
__proto__
是p的原型链,指向person
的prototype
;
constructor
即构造函数person
本身。
2. 上例中,对对象 p可以这样调用 p.toString()。toString是哪里来的? 画出原型图?并解释什么是原型链。
toString来自object.prototype
每个对象有都有属性prototype
对象都有属性proto , 对象的proto指向创建他的对象的prototype,对象的prototype的proto又指向创建对象的prototype的对象的prototype,循环下去,直到Object对象为止,因为Object.proto = null而这条链就是原型链
3. 对String做扩展,实现如下方式获取字符串中频率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
//方法一:
String.prototype.getMostOften = function() {
var Str = [];
var num = [];
var include = false;
for(var i = 0; i < this.length; i++){
if(i === 0){
Str.push(this[i]);
num.push(0);
}
for(var key in Str){
if(Str[key] === this[i]){
num[key]++;
include = true;
}
}
if(!include){
Str.push(this[i]);
num.push(1);
}
include = false;
}
var index = num.indexOf(Math.max.apply(Math, num));
return Str[index];
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
//方法二:
String.prototype.getMostOften = function() {
var Str = {};
var key ;
for(var i = 0; i < this.length; i++){
key = this[i];
if(!Str[key]){
Str[key] = 1;
}
else{
Str[key] ++;
}
}
var max = 0;
var strKey;
for(var k in Str){
if(Str[k] > max){
max = Str[k];
strKey = k;
}
}
return strKey;
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次
4. instanceOf有什么作用?内部逻辑是如何实现的?
作用:判断对象在其原型链中是否存在一个构造函数的prototype属性
function isInstanceOf(obj, fn){
var oldpro = obj.__proto__;
do{
if(oldpro === fn.prototype){
return true;
}
else {
oldpro = oldpro.__proto__;
}
}while(oldpro)
return false;
}
三、继承相关问题
1. 继承有什么作用?
1. 子类拥有父类的属性和方法,不需要重复写代码,修改时也只需修改一份代码
2. 可以重写和扩展父类的属性和代码,又不影响父类本身
2.下面两种写法有什么区别?
//方法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);
方法一的写法是属性方法都写入p1中;方法二中的p1只有属性name和sex,方法绑定在Person.prototype属性下,p1可以继承父类的属性和方法。这样做的好处是节约代码量,提高性能。
3. Object.create 有什么作用?兼容性如何?
Object.create(proto[, propertiesObject ])
创建一个拥有指定原型和若干个指定属性的对象
proto:一个对象,作为新创建对象的原型。或者为null。
propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。
4. hasOwnProperty有什么作用? 如何使用?
js秘密花园对hasOwnProperty的解释
hasOwnProperty函数
为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototype
的 hasOwnProperty
方法。
注意: 通过判断一个属性是否 undefined是不够的。 因为一个属性可能确实存在,只不过它的值被设置为 undefined。
hasOwnProperty
是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
// 修改Object.prototypeObject.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
只有 hasOwnProperty
可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。
hasOwnProperty作为属性JavaScript 不会保护hasOwnProperty
被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用外部的 hasOwnProperty
函数来获取正确的结果。
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 总是返回 false // 使用其它对象的 hasOwnProperty,并将其上下为设置为foo
{}.hasOwnProperty.call(foo, 'bar'); // true
结论
当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。 同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty方法, 这将会避免原型对象扩展带来的干扰
5. 如下代码中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
6. 补全代码,实现继承
function Person(name, sex){
// todo ...
}
Person.prototype.getName = function(){
// todo ...
};
function Male(name, sex, age){
//todo ...
}
//todo ...
Male.prototype.getAge = function(){
//todo ...
};
代码如下:
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
function Person(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(name)
}
}
Person.prototype.getName = function(){
console.log(this.name)
};
function Male(name, sex, age){
Person.call(this, name, sex);
this.age = age;
}
Male.prototype = Object.create(Person.prototype)
Male.prototype.getAge = function(){
console.log(this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
ruoyu.getAge();
ruoyu.getName();