this_原型链_继承

this相关问题

apply、call 、bind的作用以及区别

call、apply和bind方法的用法以及区别

以下代码输出什么?
var john = { 
  firstName: "John" 
}
function func() { 
  alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()

输出:John: hi!
解析:john.sayHi()可理解为john.sayHi.call(john),即this指向john,输出即为John.firstName + ":hi!"

下面代码输出什么,为什么
func() 
function func() { 
  alert(this)
}

输出:window对象
解析:func()可理解为func.call(null),浏览器里有一条规则:
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
因此上面的打印结果是 window。如果你希望这里的 this 不是 window,很简单:

func.call(obj) // 那么里面的 this 就是 obj 对象了
下面代码输出什么
document.addEventListener('click', function(e){
    console.log(this);
    setTimeout(function(){
        console.log(this);
    }, 200);
}, false);

点击页面,依次输出:document和window对象
解析:点击页面监听click事件属于方法调用,this指向事件源DOM对象,即obj.fn.apply(obj),setTimeout内的函数属于回调函数,可以这么理解,f1.call(null,f2),所以this指向window

下面代码输出什么,why
var john = { 
  firstName: "John" 
}

function func() { 
  alert( this.firstName )
}
func.call(john)

解析:call中已传入第一个参数john,即this指向John
输出:John

以下代码有什么问题,如何修改
var module= {
  bind: function(){
    $btn.on('click', function(){
      console.log(this) //this指什么
      this.showMsg();
    })
  },
  
  showMsg: function(){
    console.log('hello');
  }
}

问题:this.showMsg()的this指向$btn,而$btn上没有showMsg这个方法

解决办法有多种:

  1. this.showMsg()改为module.showMsg()module.showMsg.call(module)
  2. 在事件监听的回调函数后绑定this,即this.showMsg();}.bind(this)),bind中的this指向的是module,所以直接写bind(module)也可。
  3. 在异步操作之后this可能会发生改变,所以在这段代码中,在事件执行前将this的值保存为_this,最后通过_this.showMsg()调用方法,也可以得到正确结果
  4. 用ES6语法中的箭头函数,就不用写方法2中糟心的代码了,既然要改就全改了吧:
var module = {
    bind() {
        $btn.on('click', () => {
            console.log(this)
            this.showMsg();
        })
    },
    showMsg() {
        console.log('hello');
    }
}

原型链相关问题

有如下代码,解释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("Dot")
p.sayName();

关系:

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

前面讲面向对象的博客里,我画了这么张图,放在这里也勉强合适:

继承#禁止转载

p是Person构造函数的实例,p首先会查找自身有没有toString()这个方法,显然是没有的,所以会顺着proto原型链逐级向上查找,直到Object.prototype为止,如果还没有找到就返回null,期间找到了就调用该方法。

记住:

  • 当 new 一个构造函数的时候会创建一个实例,构造函数.prototype === 实例.__proto__
  • 一切函数都是由 Function 这个函数创建的,所以Function.prototype === 被创建的函数.__proto__
  • 一切函数的原型对象都是由 Object 这个函数创建的,所以Object.prototype === 一切函数.prototype.__proto__

表述关系为:

  • p.__proto__===Person.prototype,找到构造函数的原型,没有toString()方法于是继续查找
  • p.__proto__.__proto__===Object.prototype,找到Object的原型对象
  • p.__proto__.__proto__.toString()===Object.prototype.toString()
    最后在Object.prototype中找到了toString()方法,所以此时就可以调用toString()方法了,这也叫做继承方法。

原型链:
每个构造函数都有一个原型对象,原型对象都包含指向其构造函数的指针,而实例都包含一个指向原型对象的proto指针,我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,以此为依据,层层推进,就构成了实例与原型的链条,称为原型链。
在访问对象的属性时,如果在对象本身中没有找到,则会去原型链中逐级向上查找,找到则返回该属性,如果遍历整个链都没有找到则返回undefined。
原型链一般实现为一个链表,这样就可以按照一定的顺序来查找,原型链是实现继承的主要方法。

对String做扩展,实现如下方式获取字符串中频率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次

增加如下代码:

String.prototype.getMostOften = function () {
    var res = this.split('')
        .reduce((acc, cur) => {
            if (acc[cur]) {
                acc[cur]++
                return acc
            } else {
                acc[cur] = 1
                return acc
            }
        }, {})

    var max = ['', 0]
    for (var key in res) {
        if (res[key] > max[1]) {
            max = [key, res[key]]
        }
    }
    console.log(max)
    return max[0]
}
instanceOf有什么作用?内部逻辑是如何实现的?
  • 作用:判断一个对象是不是某个类型的实例
  • 实现:A instanceof B的判断规则是:沿着A的proto这条线来找,同时沿着B的prototype这条线来找,如果两条线逐级向上查找能找到同一个引用,返回true,证明A是B类型的实例,否则返回false。
    代码如下:
function instance(obj, type) {
    while (obj.__proto__) {
        // 以下判断条件换为 obj.__proto__.constructor === type 也可
        if (obj.__proto__ === type.prototype) {
            return true
        } else {
            // 以下return语句换乘 obj = obj.__proto__ 也可
            return instance(obj.__proto__, type)
        }
    }
    return false
}

console.log(instance([], Array))
console.log(instance(/.\d/, RegExp))
console.log(instance({}, Object))

继承相关问题

继承有什么作用?

继承机制使得不同的实例可以共享构造函数的原型对象上的属性和方法,提高了代码的复用性。

下面两种写法有什么区别?
//方法1
function People(name, sex){
    this.name = name;
    this.sex = sex;
    this.printName = function(){
        console.log(this.name);
    }
}
var p1 = new People('Dot', 2)

//方法2
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('Dot', 2);

首先要知道构造函数里定义的都是实例的属性和方法。
方法1和方法2的区别在于printName方法所在的位置,方法1中的printName方法是实例的方法,也就是说每生成一个实例之后,实例的printName就会占用内存;方法2中的printName方法定义在构造函数的原型对象上(前面说过的实例.__proto__ === 构造函数.prototype),生成的所有实例都会共享原型对象上的所有方法,节省内存,这也印证了一个结论:公共方法写在原型对象上比较好。

Object.create 有什么作用?兼容性如何?
  • 作用:Object.create()接收两个参数,作用是创建接收到的第一个参数的副本,第二个参数是可选的、额外传入副本里的属性,以第二个参数指定的任何属性都会传入副本中并覆盖已有的同名属性,但原型对象上的同名属性不会被改变。
    也就是说使用此方法时是先clone再在子类上添加自己的属性和方法,以此实现原型式继承。
    有代码如下:
var person = {
    name: 'dot',
    friends: ['a', 'b', 'c']
}

var anotherPerson = Object.create(person, {
    name: {
        value: 'dolby'
    }
})

console.log(anotherPerson.name)//dolby
console.log(person.name)//dot
  • 兼容性:各大浏览器的最新版本(包括IE9)都部署了这个方法,可以写一个polyfill解决低版本浏览器问题:
if(!Object.create){
  Object.create = function(obj){
    function F(){}
    F.prototype = obj
    return new F()
  }
}
hasOwnProperty有什么作用? 如何使用?
  • 作用:检测一个属性到底存在于原型中还是实例中,这个方法从Object继承得来,只有在属性存在于实例中才返回true
  • 使用:实例.hasOwnProperty('属性名'),返回true则属性存在于实例中,false则属性存在于原型中。
function Person() { }

Person.prototype = {
    name: 'dot',
    sex: 'female',
    age: 2,
    sayName() {
        console.log(this.name)
    }
}

var person1 = new Person()
var person2 = new Person()

console.log(person1.hasOwnProperty('name'))//false

person1.name = 'dolby'
console.log(person1.name)//dolby,来自实例
console.log(person1.hasOwnProperty('name'))//true

console.log(person2.name)//dot,来自原型
console.log(person2.hasOwnProperty('name'))//false

delete person1.name//删除实例属性,恢复实例与原型的连接
console.log(person1.name)//dot,来自原型
console.log(person1.hasOwnProperty('name'))//false
如下代码中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;
}

作用:借用构造函数实现对实例属性的继承,这样既实现了函数复用,又保证每个实例具有自己的属性。本例中将this指向Person,实现在Male中继承Person的属性

补全代码,实现继承
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 inherit(superType, subType) {
    var _prototype = superType.prototype
    _prototype.constructor = subType// 修改constructor指向
    subType.prototype = _prototype
}

function Person(name, sex) {
    this.name = name
    this.sex = sex
}

Person.prototype.getName = function () {
    console.log(this.name)
}

function Female(name, sex, age) {
    Person.call(this,name, sex)
    this.age = age
}

inherit(Person, Female)// Female继承Person

// 在继承函数之后写自己的方法,否则会被覆盖
Female.prototype.getAge = function () {
    console.log(this.age)
}

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

推荐阅读更多精彩内容