this_原型链_继承

问题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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容