JS核心知识点梳理——原型、继承(上)

栏目: JavaScript · 发布时间: 4年前

内容简介:最近又攀登了一下JS三座大山中的第二座。登山过程很酸爽,一路发现了许多之前没曾注意到的美景。本着独乐乐不如众乐乐的原则,这里和大家分享一下。一直在犹豫,到底是先讲创建对象的方法还是先讲原型。为了后面保证讲创建对象方法的连贯性,这里还是先讲讲原型吧,

JS核心知识点梳理——原型、继承(上)

引言

最近又攀登了一下JS三座大山中的第二座。登山过程很酸爽,一路发现了许多之前没曾注意到的美景。本着独乐乐不如众乐乐的原则,这里和大家分享一下。

JS的面试对象

有些人认为 JavaScript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明 (在 ES2015/ES6 中引入了 class 关键字,但那只是语法糖,JavaScript 仍然是基于原型的) 。JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。

不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。同理,原型链也是实现继承的主要方式( ES6的extends只是语法糖 )。

原型、原型链

一直在犹豫,到底是先讲创建对象的方法还是先讲原型。为了后面保证讲创建对象方法的连贯性,这里还是先讲讲原型吧,

这里为了权威,直接就摘抄 MD N的定义了

JavaScript 常被描述为一种 基于原型的语言 (prototype-based language) ——每个对象拥有一个 原型对象 ,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为 原型链 (prototype chain)

,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype属性上,而非对象实例本身。

这个__proto__属性有什么用呢?在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制,而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

简单的说,就是实例对象能通过自己的__proto__属性去访问 “类” 原型(prototype)上的方法和属性,类如果也是个实例,就会不断往上层类的原型去访问,直到找到

补充:
1.“类”的原型有一个属性叫做constructor指向“类”
2.__proto__已被弃用,提倡使用Object.getPrototypeOf(obj)

举例:

var arr = [1,2,3] //arr是一个实例对象(数组类Array的实例)
arr.__proto__ === Array.prototype //true  实例上都有一个__proto__属性,指向“类”的原型
Array.prototype.__proto__ === Object.prototype //true “类”的原型也是一个Object实例,那么就一定有一个__proto__属性,指向“类”object的原型

这里补充一个知识点:

浏览器在在Array.prototype上内置了pop方法,在Object.prototype上内置了toString方法

JS核心知识点梳理——原型、继承(上)

上图是我画的一个原型链图

[1,2,3].pop() //3
[1,2,3].toString() //'1,2,3'
[1,2,3].constructor.name //"Array" 
[1,2,3].hehe() //[1,2,3].hehe is not a function

当我们调用pop()的时候,在实例[1,2,3]上面没有找到该方法,则沿着原型链搜索"类"Array的原型,找到了pop方法并执行,同理调用toString方法的时候,在"类"Array没有找到则会继续沿原型链向上搜索"类"Object的原型,找到toString并执行。

当执行hehe方法的时候,由于“类”Object的原型上并没有找到,搜索“类”Object的__proto__,由于执行null,停止搜索,报错。

注意,[1,2,3].constructor.name显示‘Array’不是说明实例上有constructor属性,而是正是因为实例上没有,所以搜索到的原型上了,找到了constructor

类,创建对象的方法

怎么创建对象,或者说怎么模拟类。这里我就不学高程一样,给大家介绍7种方法了,只讲我觉得必须掌握的。毕竟都es6 es7了,很多方法基本都用不到,有兴趣自己看高程。

利用构造函数

const Person = function (name) {
        this.name = name
        this.sayHi = function () {
            alert(this.name)
        }
    }
    const xm = new Person('小明')
    const zs = new Person('张三')
    zs.sayHi() //'张三'
    xm.sayHi() //'小明'

缺点: 每次实例化都需要复制一遍函数到实例里面。但是不管是哪个实例,实际上sayHi都是相同的方法,没必要每次实例化的时候都复制一遍,增加额外开销。

寄生构造函数模式

function specialArray() {
        var arr = new Array()
        arr.push.apply(arr,arguments)
        arr.sayHi = function () {
            alert('i am an specialArray')
        }
        return arr
    }
    var arr = new specialArray(1,2,3)

这个和在数组的原型链上增加方法有啥区别? 原型链上增加方法,所有数组都可以用。寄生构造函数模式只有被specialArray类new出来的才能用。

组合使用原型和构造函数

//共有方法挂到原型上
    const Person = function () {
         this.name = name
    }
    Person.prototype.sayHi = function () {
            alert(this.name)
        }
    const xm = new Person('小明')
    const zs = new Person('张三')
    zs.sayHi() //'张三'
    xm.sayHi() //'小明'

缺点:基本没啥缺点了,创建自定义类最常见的方法, 动态原型模式 也只是在这种混合模式下加了层封装,写到了一个函数里面,好看一点,对提高性能并没有卵用。

es6的类

es6的‘类’class其实就是语法糖

class Person {
   constructor(name) {
       this.name = name
  }
  say() {
       alert(this.name)
  }
}
const xm = new Person('小明')
const zs = new Person('张三')
zs.sayHi() //'张三'
xm.sayHi() //'小明'

在es2015-loose模式下用bable看一下编译

"use strict";

var Person =
/*#__PURE__*/
function () {
  function Person(name) {
    this.name = name;
  }

  var _proto = Person.prototype;

  _proto.say = function say() {
    alert(this.name);
  };

  return Person;
}();

分析:严格模式,高级单例模式封装了一个类,实质就是 组合使用原型和构造函数

JS世界里的关系图

JS核心知识点梳理——原型、继承(上)

知识点:

  1. Object.getPrototypeOf(Function) === Function.prototype // Function是Function的实例,没毛病
  2. Object.getPrototypeOf(Object.prototype)
  3. 任何方法上都有prototype属性以及__proto__属性
    任何对象上都有__proto__属性
  4. Function.__proto__.__proto__===Object.prototype
  5. Object.getPrototypeOf(Object)===Function.prototype
  6. 最高级应该就是Function.prototype了,因为5

判断类型的方法

之前在 JS核心知识点梳理——数据篇 里面说了一下判断判断类型的四种方法,这里借着原型再来分析一下

1.typeof:

只能判断基础类型中的非Null,不能判断引用数据类型(因为全部为object)它是操作符

2.instanceof:

用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置 风险的话有两个

//判断不唯一
[1,2,3] instanceof Array //true
[1,2,3] instanceof Object //true
//原型链可以被改写
const a = [1,2,3]
a.__proto__ = null
a instanceof Array //false

仿写一个instanceof,并且挂在Object.prototype上,让所有对象都能用

//仿写一个instance方法
    Object.prototype.instanceof = function (obj) {
        let curproto = this.__proto__
        while (!Object.is(curproto , null)){
            if(curproto === obj.prototype){
                return true
            }
            curproto = curproto.__proto__
        }
        return false
    }
   
[1,2,3].instanceof(Array) //true
[1,2,3].instanceof(Object) //true
[1,2,3].instanceof(Number) //false
[1,2,3].instanceof(Function) //false
1..instanceof(Function) //false
(1).instanceof(Number) //true

3.constructor:

constructor 这玩意已经介绍过了,“类”的原型执行constructor指向“类”

风险的话也是来自原型的改写

[1,2,3].constructor.name //'Array'

// 注意下面两种写法区别
Person.protorype.xxx = function //为原型添加方法,默认constructor还是在原型里
Person.protorype = { //原型都被覆盖了,没有constructor了,所要要手动添加,要不然constructor判断失效
   xxx:function
   constructor:Person
}

4.Object.prototype.toString.call(xxx)

试了下,好像这个方法也不是很准

null 可以用object.is(xxx,null)代替

Array 可以用Array.isArray(xxx)代替

Object.prototype.toString.call([1,2,3])   //"[object Array]"
Object.prototype.toString.call(function(){}) //"[object Function]"
Object.prototype.toString.call(1) //"[object Number]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call({}) //"[object Object]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(true) // 特别注意 特别注意 特别注意"[object Object]"
Object.prototype.toString.call('string') //  特别注意 特别注意 特别注意 "[object Undefined]"

总结

参照各种资料,结合自己的理解,在尽量不涉及到继承的情况下,详细介绍了原型及其衍生应用。由于本人技术有限,如果有说得不对的地方,希望在评论区留言。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Sovereign Individual

The Sovereign Individual

James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00

Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具