博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
包教不包会系列-javascript 原型链与继承
阅读量:4692 次
发布时间:2019-06-09

本文共 8692 字,大约阅读时间需要 28 分钟。

前言

本来一直犹豫要不要写原型相关的东西,思虑再三还是决定水一文 ???

JavaScript 基于原型实现了继承,所以了解原型及原型链还是很有必要的。

原型

推荐看看 和

javaScript 的原型是什么?

当提到原型的时候,我们会想到 __proto__ 和 prototype 这两个老生常谈的属性。我们的脑海可能会复现这一样一句话:

每个对象都有 __proto__ 属性,Function 会自带 prototype 属性。查找 obj 有属性的时候,会先从 obj 自身查找,找不到会去 obj 的原型去找,obj 的原型找不到会去 obj 的原型的原型去找(在 obj 的原型链查找)。

那么问题来了,到底从 __proto__ 和 prototype 中那个去寻找呢?

const obj = {};// 仅演示使用了属性,推荐使用 Object.setPrototypeOf(),设置对象原型obj.__proto__ = {    name: '__proto__',};obj.prototype = {    name: 'prototype',};console.log(obj.name);复制代码

一个对象的原型指的是 __proto__ 属性。 推荐使用 Object.getPrototypeOf() 获得对象的原型,Object.setPrototypeOf() 设置对象的原型

prototype 是什么,且听下文分解? ???

基于原型实现继承

const obj = {};const objProto = {    name: '__proto__',    obj: { name: 1 },};objProto.__proto__ = {    getName() {        console.log(this.name);    },};obj.__proto__ = objProto;console.log(obj.name);// __proto__obj.getName();// __proto__复制代码

继承很简单,只要建立其原型,形成原型链(obj.__proto__.__proto__.__proto__)。

构造函数实现继承

构造函数及 new 操作符

可以大致看下,加深对 js 的函数理解及理解 new 到底做了什么

const Foo = function _Foo(name = '默认') { this.name = name; };const foo = new Foo();console.log(foo.name);复制代码
  • new Foo() 过程简单说明
// 1. 首先创建一个空对象const obj = {};// 2. 对 obj.__proto 赋值obj.__proto__ = Foo.prototype instanceof Object ? Foo.prototype : Object;// 3. 更改构造器函数内部this,将其指向新创建的空对象const result=Foo.apply(obj,arguments);// 4.返回对象if(result instanceof Object){    return result;}return obj;复制代码
  • 代码验证 new Foo()
const Foo = function _Foo(name = '默认') {    this.name = name;    // 返回不是对象    return 1;};const foo = new Foo();console.log(foo.name);// 默认const Foo1 = function _Foo1(name = '默认') {    this.name = name;    return { age: 1 };};const foo1 = new Foo1();console.log(foo1.age);// 1复制代码
  • 模拟 new 操作过程
function New(func, ...data) {    const obj = {};    obj.__proto__ = func.prototype;    const result = func.apply(obj, data);    if (result instanceof Object) {        return result;    }    return obj;}const Foo = function _Foo(name = '默认') {    this.name = name;    return 1;};const foo = new Foo();console.log(foo.name);const Foo1 = function _Foo1(name = '默认') {    this.name = name;    return { age: 1 };};const Foo2 = function _Foo2(name = '默认') {    this.name = name;};Foo2.prototype.log = function _log() {    console.log('测试继承 prototype');};const ret = New(Foo, '测试');console.log(ret.name);// 测试const ret1 = New(Foo1, '测试返回 obj');console.log(ret1.age);// 1const ret2 = New(Foo2, '测试正常构造');console.log(ret2.name);// 测试正常构造ret2.log();// 测试继承 prototype复制代码

基于构造函数和原型实现继承

// 1.通过原型链和构造函数实现继承,A 继承 B ,B 继承 Cconst C = function _C(roles = ['徐晓', '吴素', '徐脂虎', '徐渭熊', '徐凤年', '徐龙象']) {    this.roles = roles;    this.c = function _c() {        return '自身 c';    };};C.prototype.getRoles = function _getRoles() {    return `${
this.roles}-C.getRoles`;};const B = function _B(obj, roles) { // 敲黑板,划重点,B 继承 C ,要在 B 的构造函数调用 C 的构造函数继承 C 中的属性 C.call(this, roles); this.obj = obj; this.b = function _b() { return '自身 b'; };};// 将 B 的原型对象指向 C 的实例。B.prototype = new C();B.prototype.constructor = B;B.prototype.study = function _study() { return `${
this.name}-B.study`;};// A 重写了 B 中的方法 bconst A = function _A(name, obj, roles) { B.call(this, obj, roles); this.name = name; this.play = function _play() { return `${
this.name}-A.paly`; }; this.b = function _bb() { return `${
this.name}-A重写b方法`; };};A.prototype = new B();A.prototype.constructor = A;A.prototype.getName = function _getName() { return this.name;};const a1 = new A('红薯', { age: 18 }, ['徐凤年']);const a2 = new A('青鸟', { age: 18 }, ['吴素']);console.log(a1.play()); // 红薯-A.palyconsole.log(a2.play()); // 青鸟-A.palyconsole.log(a1.getName()); // 红薯console.log(a2.getName()); // 青鸟console.log(a1.study()); // 红薯-B.studyconsole.log(a2.study()); // 青鸟-B.studyconsole.log(a1.getRoles()); // 徐凤年-C.getRolesconsole.log(a2.getRoles()); // 吴素-C.getRolesconsole.log(a2 instanceof B); // trueconsole.log(a1 instanceof B); // trueconsole.log(a1 instanceof C); // trueconsole.log(a1 instanceof C); // trueconsole.log(a1.b()); // 红薯-A重写b方法console.log(a1.c()); // 自身 cconsole.log(a2.b()); // 青鸟-A重写b方法console.log(a2.c()); // 自身 c复制代码

基于原型链和构造函数实现的继承已经不错了,但是 A 继承 B 的时候 B.call(this, obj, roles);A.prototype = new B();调用了两次造成内存浪费。所以有大神站了出来,搞了一个寄生继承。

寄生继承

  • 用中间空对象桥接,实现继承
const MyExtends = function _MyExtends(Son, Parent) {    const F = function _F() {};    const f = new F();    f.__proto__ = Parent.prototype;    Son.prototype = f;    Son.prototype.constructor = Son;};复制代码
  • 寄生继承尝试
const MyExtends = function _MyExtends(Son, Parent) {    const F = function _F() { };    const f = new F();    f.__proto__ = Parent.prototype;    Son.prototype = f;    Son.prototype.constructor = Son;};const C = function _C(roles = ['徐晓', '吴素', '徐脂虎', '徐渭熊', '徐凤年', '徐龙象']) {    this.roles = roles;    this.c = function _c() {        return '自身 c';    };};C.prototype.getRoles = function _getRoles() {    return `${
this.roles}-C.getRoles`;};const B = function _B(name, roles) { C.call(this, roles); this.name = name; this.b = function _b() { return '自身 b'; };};B.prototype.study = function _study() { return `${
this.name}-B.study`;};MyExtends(B, C);const b2 = new B('陈平安', ['文圣弟子']);const b1 = new B('宁姚', ['平安的老婆']);console.log(b2.roles);// ['文圣弟子']console.log(b1.roles);// ['平安的老婆']复制代码

es6 的新特性 class 解决了繁琐的写法

强力推荐看看阮一峰的 。买的实体书没看多少,网站上的 doc 看了好几遍,就当支持大神开源了

你有没有好奇 class 是什么呢?

// class 这种写法是语法糖class Foo {    constructor(name) {        this.name = name;    }    getAge () {        return this.age;    }    static age = 1;    static getAge () {        return this.age;    }}class Son extends Foo{    constructor (age, name) {        super(name);        this.age;    }}console.log(typeof Foo);// functionconsole.log(Foo instanceof Function);// true复制代码

class 和 function 静态方法和静态属性对应

jQuery() 源码解析

下面这个问题困扰好久,一直想不明白为什么写的这么复杂呢?原来是为了安全调用构造函数,new 和 不 new 可以达到同样的效果。

const jQuery = function _jQuery(name = 'jQuery') {    return new jQuery.fn.init(name);};jQuery.getAge = function _getAge() {    console.log('getAge');};jQuery.prototype.getName = function _getName() {    return this.name;};jQuery.fn = jQuery.prototype;// 这种写法就是为了安全调用构造函数,new 与不 new 达到同样的效果const init = jQuery.fn.init = function _init(name) {    this.name = name;};init.prototype = jQuery.fn;// 基于 new 创建对象const jq1 = new jQuery('jq1-创建');// 直接执行函数,返回对象const jq2 = jQuery('jq2-创建');console.log(jq1.getName());console.log(jq2.getName());复制代码

typeof 和 instanceof

// 判断 undefinedconst undefinedData = undefined;console.log(typeof undefinedData);// undefined// 判断 null,null 是基础数据类型const nullData = null;console.log(typeof nullData);// object// es6 新出来的另一种基础类型,很有用,推荐看看const symbol = Symbol.for('testSymbol');console.log(typeof symbol);// symbolconsole.log('symbol instanceof Symbol:', symbol instanceof Symbol);// false// 判断 String 字符串const str = '1';console.log(typeof str);// stringconsole.log('str instanceof String', str instanceof String);// falseconst strObj = new String('222');// stringconsole.log('strObj instanceof String', strObj instanceof String);// true// 判断 Number 数字const num = 1;console.log(typeof num);// numberconsole.log('num instanceof Number:', num instanceof Number);// falseconst numObj = new Number(1);console.log(typeof numObj);// numberconsole.log('numObj instanceof Number:', numObj instanceof Number);// true// 判断 Objectconst obj = {};console.log(typeof obj);// objectconsole.log('obj instanceof Object:', obj instanceof Object);// true// 判断 Functionconst func = function _func() {};console.log(typeof func);// functionconsole.log('func instanceof Function:', func instanceof Function);// 判断 Arrayconst arr = [1, 2];console.log(typeof arr);// objectconsole.log('arr instanceof Array:', arr instanceof Array);// ture// 判断 Dateconst date = new Date();console.log(typeof date);// objectconsole.log('date instanceof Date:', date instanceof Date);// true// 判断 Mapconst map = new Map();console.log(typeof map);// objectconsole.log('map instanceof Map:', map instanceof Map);// true// 判断 Setconst set = new Set();console.log(typeof set);// objectconsole.log('set instanceof Set:', set instanceof Set);// true复制代码

typeof 返回类型的小写字符串可以判断 undefined(undefined),String(string),Number(number),Symbol(symbol),Function(function) 类型,其余的都会返回 object

null 为基础类型,但是 typeof null 返回 object

  • instanceof

instanceof 是基于原型链判断的

obj1 instanceof obj2 返回 true 的条件:obj2 为 function 并且在 obj1 的原型链中能找到 obj2.prototye 就会返回 true

// 代码验证上面的结论,颠覆了认知 ???const obj1 = {};// obj2 不为 function 会报错const obj2 = function _obj2() { };const tempPrototype = { name: 1 };obj2.prototype = tempPrototype;obj1.__proto__ = obj2.prototype;console.log(obj1 instanceof obj2); // true复制代码

转载于:https://juejin.im/post/5d4e476af265da03d7280a1f

你可能感兴趣的文章
UVA 116 Unidirectional TSP (白书dp)
查看>>
第三方测速工具
查看>>
MySQL 网络访问连接
查看>>
在aws ec2上使用root用户登录
查看>>
数据访问 投票习题
查看>>
CIO知识储备
查看>>
cnblog!i'm coming!
查看>>
使用点符号代替溢出的文本
查看>>
Axios 中文说明
查看>>
fatal: remote origin already exists.
查看>>
gridview 自定义value值
查看>>
2018二月实现计划成果及其三月规划
查看>>
类名.class和getClass()区别
查看>>
12/17面试题
查看>>
LeetCode 242. Valid Anagram
查看>>
JSP表单提交乱码
查看>>
如何适应现代雇佣关系
查看>>
团队项目(第五周)
查看>>
SQL 优化经验总结34条
查看>>
开源 视频会议 收藏
查看>>