面试题 -- new及原型链

new

首先要搞清楚什么是new,new是javascript中的一种操作符,作用是通过构造函数去创建一个实例对象。

function Foo(name) {
    this.name = name;
}
console.log("new Foo('mm')的类型:",typeof new Foo('mm')); // object
console.log("Foo的类型:",typeof Foo); // function

new出的结果是一个对象,它都做了些什么,中间的过程是什么?

new所做及操作过程,总结一下就是:一建、二连、三改、四返回
一建:创建一个新的对象
二连:将当前新对象的原型__proto__指向构造函数的原型对象;
三改:通过构造函数的相关方法,将this的原本指向window,调整为当前对象
四返回:将对象返回

 有些大佬给出了new的过程分析,记录一下地址:

JS 的 new 是个啥? - 徐航宇 - 博客园
JS中的new操作符 - 见嘉于世 - 博客园

原型链

原型本质上就是一个对象。实例对象拥有__proto__属性,构造函数拥有prototype原型属性,它们两个的属性都会指向同一个对象,这个对象就是原型对象,它们都拥有着与之相应的相同属性。

那么原型链呢,原型链就是以原型对象为核心,向外扩展的类似链条一样,由对象与构造函数所构成的集合。

原型链的形成/过程:
当查找某个对象或某个构造函数的某个属性时,先会查找自身,当自身不存在该属性,会根据自身的__proto__或prototype属性,向它的原型查找;
如果它的原型中也不存在,那么会重复之前的步骤,再向原型的原型中查找,直到最终Object.prototype.proto返回null,则停止查找。

函数的原型属性 —— prototype
对象的原型属性 —— proto
构造函数new的隐式原型 —— __proto__

函数也是对象,也拥有proto属性;默认情况下,所有原型对象会自动获得constructor属性,包含指向prototype属性所在的函数

很多的文章中都给到了图注,我只是记录一下自己的理解,可能我本身对与它的理解也还是有点不对的地方,对于更详细的,感兴趣的话可以看一下
javascript——原型与原型链 - 雅昕 - 博客园
说说原型(prototype)、原型链和原型继承 - 知乎
原型链以及继承的几种方式 - 酸菜鱼i - 博客园

到这不得不再记录一下原型链的继承。继承是指一个对象直接使用另外一个对象的属性和方法。

以下内容为来源于:

原型链继承

实现原理:

  • 原型链继承让子类的prototype指向父类的实例;
function Super(name) {
    this.name = name
    this.a = [1, 2, 3, 4]
}

Super.prototype.say = function () {
    return 22
}

function Sub() { }
Sub.prototype = new Super() // 原型链继承就是让子类的prototype指向父类的实例
Sub.prototype.constructor = Sub; // 再让prototype的constructor重新指向它关联的构造函数。用来补全原型(prototype),每个prototype原型都有一个constructor属性,指向它关联的构造函数

const sub1 = new Sub();
const sub2 = new Sub();

console.log(sub1.a) // [1, 2, 3, 4]
console.log(sub1.say()) // 22

缺陷:

1.引用值共享问题;也就是说父类属性一旦复制给子类的原型属性,此时属性属于子类共享属性 --继承者的实例间篡改;

  • 例如:sub1.a.push(5) console.log(sub1.a) ==> [1, 2, 3, 4, 5] console.log(sub2.a) ==> [1, 2, 3, 4, 5]

2.实例化子类时 无法向父类传参

  • 例如:const sub1 = new Sub('王者'); console.log(sub1.name) ==》 undefined

如何解决原型链的问题呢?

构造函数继承

实现原理:

  • 在子类的构造函数内部,借用call() 或者 apply() 方法,调用超类型的构造函数
  • 通过call或者aply方法,将父对象的构造函数绑定在子对象上
function Super(name) {
    this.name = name
    this.a = [1, 2, 3, 4]
}

Super.prototype.say = function () {
    return 22
}

function Sub(arg) {
    //使用call或者aply方法,将父对象的构造函数绑定在子对象上;
    Super.call(this, arg)
}

const sub1 = new Sub('5');
const sub2 = new Sub();
sub1.a.push(5)

console.log(sub1.name) // '5'
console.log(sub1.a) // [1,2,3,4,5]
console.log(sub2.a)// [1,2,3,4]

缺陷:

  • 没有办法拿到原型上的方法

例如 :console.log(sub1.say()) ==> Uncaught TypeError: sub1.say is not a function

如何解决原型链上的共享方法就无法被读取继承?

组合继承

实现原理:

  • 指的是将原型链和借用构造函数的技术组合到一块,从而返回二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
function Super(name) {
    this.name = name
    this.a = [1, 2, 3, 4]
}

Super.prototype.say = function () {
    return 22
}

function Sub(arg) {
    //使用call或者aply方法,将父对象的构造函数绑定在子对象上;
    Super.call(this, arg)
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

const sub1 = new Sub('5');
const sub2 = new Sub('8');
console.log(sub1.name) // '5'
console.log(sub2.name) // '8'
console.log(sub1.say()) // 22

缺陷:

  • 父类的构造函数都会被执行两次

例如: new Super() 执行一次, Super.call(this, arg)执行一次

如何解决组合继承消耗内容资源问题?

寄生组合继承

基本思路:

不必为了指定子类型的原型而调用超类型的构造函数,因为我们所需要的只是超类型原型的一个副本而已;

function Super(name) {
    this.name = name
    this.a = [1, 2, 3, 4]
}

Super.prototype.say = function () {
    return 'Super'
}

function Sub(arg) {
    //使用call或者aply方法,将父对象的构造函数绑定在子对象上;
    Super.call(this, arg)
}

// 兼容低版本es5以下版本 不存在 Object.create 方法
if (!Object.create) {
    Object.create = function (proto) {
        function F() { };
        F.prototype = proto;
        return new F() // 这里我们要返回的是一个实例对象;
    }
}


Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;

const sub1 = new Sub('5');
const sub2 = new Sub('8');
console.log(sub1.name) // '5'
console.log(sub2.name) // '8'
console.log(sub1.say()) //  Super

提高: 多重继承

function Game(name) {
        this.name = 'LOL';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    function Store() {
        this.shop = 'steam';
    }
    Store.prototype.getPlatform = function() {
        return this.shop;
    }

    function LOL(arg) {
        Game.call(this, arg);
        Store.call(this, arg);
    }

    LOL.prototype = Object.create(Game.prototype);
    Object.assign(LOL.prototype, Store.prototype);
    LOL.prototype.constructor = LOL;
    // LOL继承了两个类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值