this_原型链_继承

一、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的原型链,指向personprototype
constructor即构造函数person本身。

2. 上例中,对对象 p可以这样调用 p.toString()。toString是哪里来的? 画出原型图?并解释什么是原型链。

toString来自object.prototype
每个对象有都有属性prototype
对象都有属性proto , 对象的proto指向创建他的对象的prototype,对象的prototype的proto又指向创建对象的prototype的对象的prototype,循环下去,直到Object对象为止,因为Object.proto = null而这条链就是原型链

this_原型链_继承_第1张图片

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);
this_原型链_继承_第2张图片
方法一
this_原型链_继承_第3张图片
方法二

方法一的写法是属性方法都写入p1中;方法二中的p1只有属性name和sex,方法绑定在Person.prototype属性下,p1可以继承父类的属性和方法。这样做的好处是节约代码量,提高性能。

3. Object.create 有什么作用?兼容性如何?

Object.create(proto[, propertiesObject ])创建一个拥有指定原型和若干个指定属性的对象

proto:一个对象,作为新创建对象的原型。或者为null。

propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。

this_原型链_继承_第4张图片
兼容性

4. hasOwnProperty有什么作用? 如何使用?

js秘密花园对hasOwnProperty的解释
hasOwnProperty函数
为了判断一个对象是否包含自定义属性而不是原型链上的属性, 我们需要使用继承自 Object.prototypehasOwnProperty方法。

注意: 通过判断一个属性是否 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方法, 这将会避免原型对象扩展带来的干扰

this_原型链_继承_第5张图片
JavaScript 秘密花园

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();
this_原型链_继承_第6张图片
继承

你可能感兴趣的:(this_原型链_继承)