一、相关资料

  1. JS原型链与继承别再被问倒了
  2. js构造函数详解
  3. 一步步图解javascript的原型(prototype)对象,原型链
  4. 进阶必读:深入理解 JavaScript 原型
  5. JS原型链简单图解
  6. JS 中 this 指向问题
  7. 通俗易懂之JavaScript手动实现apply方法

二、技术点

1.构造函数(constructor)

(1)构造函数与普通函数的区别

主要从功能上进行区别的,构造函数的主要功能为初始化对象,特点是和new 一起使用。new就是在创建对象,从无到有,构造函数就是在为初始化的对象添加属性和方法。

(2)构造函数例子
function Father(name) {this.name = namethis.sayWorld = function () {console.log(`Hello ${this.name}!`)}
}Father.prototype.getName = function(){return this.name
}
(3)对new的理解

new 申请内存, 创建对象,当调用new时,后台会隐式执行new Object()创建对象。所以,通过new创建的字符串、数字是引用类型,而是非值类型。

(4)手写new函数
function myNew(constructFunction, ...args) {// 先用Object创建一个空的对象,const obj = Object.create(null)// 继承目标函数的原型obj.__proto__ = constructFunction.prototype// 改变this指向const res = constructFunction.apply(obj, args)// const res = constructFunction.call(obj, ...args)//正常规定,如何fn返回的是null或undefined(也就是不返回内容),我们返回的是obj,否则返回relreturn res instanceof Object ? res : obj
}const fa1 = myNew(Father, '小明')
fa1.sayWorld()
(5)ES6 中 class 与构造函数的关系

class 为构造函数的语法糖,即 class的本质是构造函数。class 的继承 extends 本质为构造函数的原型链的继承。

类的写法

class Person{  //定义一个名字为Person的类constructor(name,age){ //constructor是一个构造方法,用来接收参数this.name = name;  //this代表实例对象this.age = age;}say(){  //这是一个类的方法,注意千万不要加上functionreturn   this.name + this.age}
}var obj = new Person('老铁',18);
console.log(obj.say());

构造函数的写法

function Person(name,age){  if(!(this instanceof Person)){ //避免使用者不小心讲Person当作普通函数执行throw new Error(''请使用 new Person"); //仿ES6 class 中的写法}this.name = name;this.age = age;
}
Person.prototype.say = function(){return   this.name + this.age
}var obj = new Person('老铁',18);   //通过构造函数创建对象,必须使用new运算符
console.log(obj.say());

2.原型对象(prototype)

function CreateObj(uName) {this.userName = uName;}CreateObj.prototype.showUserName = function(){return this.userName;}var obj1 = new CreateObj('ghostwu');var obj2 = new CreateObj('卫庄');

(1)每个函数都有一个原型属性(prototype) , 这个属性是一个指针,指向构造函数的原型对象( CreateObj.prototype)。

(2)在默认情况下,所有原型对象都会自动获得一个constructor属性,该属性包含一个指向prototype属性所在的函数。

(3)所有的实例( 通过构造函数new出来的,原型对象[ 如CreateObj.prototype]等)都包含一个隐式原型(__proto__),该指针指向实例的构造函数的原型对象,

obj1.__proto__ === CreateObj.prototype //trueobj2.__proto__ === CreateObj.prototype //true

(4)当一个的对象访问属性和方法的时候,她的访问规则叫(就近原则),规则如下:

  • 当实例上面,存在属性或者方法时,直接用实例上面的。
  • 如果实例上面不存在属性和方法,就会沿着实例的__proto__指针指向的原型对象继续往上查找,如果找不到,值就是undefined
原型链

所有的实例(包括原型对象)都有一个隐式原型__proto__,CreateObj.prototype.__proto__指向的是Object.prototype, 通过 全等运算符 (===) 测试之后为true。

这就是原型链,通过隐式原型把一些构造函数层层的串起来。

构造函数,原型和实例的关系图如下:

每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针。

图解构造器Function和Object的关系:

//①构造器Function的构造器是它自身
Function.constructor=== Function;//true//②构造器Object的构造器是Function(由此可知所有构造器的constructor都指向Function)
Object.constructor === Function;//true//③构造器Function的__proto__是一个特殊的匿名函数function() {}
console.log(Function.__proto__);//function() {}//④这个特殊的匿名函数的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函数
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true

3.this指向

口诀:箭头函数、new、bind、apply 和 call、欧比届点(obj.)、直接调用、不在函数里。

(1).箭头函数
要点:
  • 创建箭头函数时,就已经确定了它的 this 指向。
  • 箭头函数内的 this 指向外层的 this。
(2).new
要点:
  • 当使用 new 关键字调用函数时,函数中的this 一定是JS创建的新对象。
  • 箭头函数不能当做构造函数,所以不能与 new 一起执行。
(3).bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

const module = {x: 42,getX: function() {return this.x;}
};const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefinedconst boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
要点:
  • 多次bind 时只认第一次的 bind 的值
function func() {console.log(this)
}func.bind(1).bind(2)() // 1
  • 箭头函数中 this 不会被修改
func = () => {// 这里 this 指向取决于外层 this,参考口诀 7 「不在函数里」console.log(this)
}func.bind(1)() // Window,口诀 1 优先
  • bindnew
function func() {console.log(this, this.__proto__ === func.prototype)
}boundFunc = func.bind(1)
new boundFunc() // Object true,口诀 2 优先
(4).apply 和 call

apply()call() 的第一个参数都是 this,区别在于通过 apply 调用时实参是放到数组中的,而通过 call 调用时实参是逗号分隔的。

代码实现 apply 方法
function myApply(obj){const target = obj || window;const fn = Symbol['fn'];target[fn] = this;const para = arguments[1] || [];const result = target[fn](...para);delete target[fn];return result;
}/*** 测试myApply ***/
Function.prototype.myApply = myApply;
function printName(para1,para2){console.log(this.name,para1,para2);
}
var obj1 = {name:'tom'};
printName.myApply(obj1,['good','boy']); // 输出tom good boy
代码实现 call 方法
Function.prototype.myCall = function(context){// 在 Function 构造函数的原型上添加我们自己的call方法:myCallconst content = context || window; /* 若有传参,则为context;若无传参,则为window。例如showName2.myCall(obj),则content就指向obj */const fn = Symbol(); // 这里用Symbol类型(ES6新增基本类型)声明一个变量,确保独一无二,作为接下来给content新添加属性的keycontent[fn] = this; /* 这里的this,谁调用myCall方法,指的就是谁。例如showName2.myCall(obj),执行完这行代码,即给obj新增了一个fn属性,值为showName2函数 */const result = content.fn(...Array.from(arguments).slice(1)); /* 通过content.fn()方式调用方法,this被绑定为content即obj。把参数传递进去就可以了,...是ES6的扩展运算符,Array.from()也是ES6语法,把类数组对象转为数组,因为第一个参数是this,所以把它剥掉 */delete content[fn]; // 删除添加的临时属性return result;
}var showName2 = function() { // ES5中函数声明方式console.log(this.name);
};showName2.myCall(obj); // 输出'Bob'
要点:
  • 箭头函数中 this 不会被修改。
func = () => {// 这里 this 指向取决于外层 this,参考口诀 7 「不在函数里」console.log(this)
}func.apply(1) // Window,口诀 1 优先
  • bind 函数中 this 不会被修改。
function func() {console.log(this)
}boundFunc = func.bind(1)
boundFunc.apply(2) // 1,口诀 3 优先
(5).欧比届点(obj.)
function func() {console.log(this.x)
}obj = { x: 1 }
obj.func = func
obj.func() // 1
(6).直接调用

在函数不满足前面的场景,被直接调用时,this 将指向全局对象。在浏览器环境中全局对象是 Window,在 Node.js 环境中是 Global

function func() {console.log(this)
}func() // Window
function outerFunc() {console.log(this) // { x: 1 }function func() {console.log(this) // Window}func()
}outerFunc.bind({ x: 1 })()
(7).不在函数里

不在函数中的场景,可分为浏览器的 <script /> 标签里,或 Node.js 的模块文件里。

  • <script /> 标签里,this 指向 Window
  • Node.js 的模块文件里,this 指向 Module 的默认导出对象,也就是 module.exports

4.继承

(1). 经典继承(借用构造函数)

原型链有两个问题

问题一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;问题二: 在创建子类型(例如创建Son的实例)时,不能向超类型(例如Father)的构造函数中传递参数.

基本思想

基本思想:即在子类型构造函数的内部调用超类型构造函数.

代码示例

function Father(){this.colors = ["red", "yellow", "green"];
}
function Son (){Father.call(this); //继承了Father,且向父类型传递参数
}var instance1 = new Son();
instance1.colors.push("black");
console.log(instance1.colors); // "red,yellow,green,black"var instance2 = new Son();
console.log(instance2.colors); // "red,yellow,green" 可见引用类型值是独立的
(2). 组合继承

采用经典继承有个问题

仅仅借用构造函数,那么将无法避免构造函数模式存在的问题--方法都在构造函数中定义, 因此函数复用也就不可用了.而且超类型(如Father)中定义的方法,对子类型而言也是不可见的. 考虑此,借用构造函数的技术也很少单独使用.

基本思想

基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.

代码示例

function Father(name){this.name = name;this.colors = ["red", "yellow", "green"];
};
Father.prototype.sayName = function(){alert(this.name);
};
function Son (name, age){Father.call(this, name); //继承实例属性,第一次调用Father()this.age = age;
}
Son.prototype = new Father(); // 继承父类方法,第二次调用Father()
Son.prototype.sayAge = function(){alert(this.age);
}var instance1 = new Son("louis",5);
instance1.colors.push("black");
console.log(instance1.colors); // "red,yellow,green,black"
instance1.sayName(); // louis
instance1.sayAge(); // 5var instance2 = new Son("zhai", 10);
console.log(instance2.colors); // "red,yellow,green" 可见引用类型值是独立的
instance2.sayName(); // "zhai"
instance2.sayAge(); // 10
(3). 原型继承

基本思想

在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.

引发问题

从本质上讲, object() 返回了一个引用传入对象的新对象. 这样可能带来一些共享数据的问题

代码示例


function object(o){function F(){};F.prototype = o;return new F();
}var Person = {friends: ["Van", "Louis", "Nick"]
};var anotherPerson = object(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends); // "Van,Louis,Nick,Rob,Style"

优化

  • ES5中,新增object.create()方法规范了上面的原型式继承

object.create() 接收两个参数:

  • 一个用作新对象原型的对象
  • (可选的)一个为新对象定义额外属性的对象
var person = {friends : ["Van","Louis","Nick"]
};
var anotherPerson = Object.create(person);
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.friends.push("Style");
alert(person.friends);//"Van,Louis,Nick,Rob,Style"
(4). 寄生继承

基本思想

寄生式继承的思路与(寄生)构造函数和工厂模式类似, 即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象. 如下.

代码示例


function createAnother(original){var clone = object(original);//通过调用object函数创建一个新对象clone.sayHi = function(){//以某种方式来增强这个对象alert("hi");};return clone;//返回这个对象
}

这个例子中的代码基于person返回了一个新对象--anotherPerson. 新对象不仅具有 person 的所有属性和方法, 而且还被增强了, 拥有了sayH()方法.

(5). 寄生组合式继承

采用组合继承有个问题

组合继承最大的问题就是无论在什么情况下,都会调用两次弗雷构造函数,一次是在创建子类原型的时候,另一次是在子类型构造函数内部。

基本思想

不必为了指定子类型的原型而调用超类型的构造函数。

代码示例

function extend(subClass,superClass){var prototype = object(superClass.prototype);//创建对象prototype.constructor = subClass;//增强对象subClass.prototype = prototype;//指定对象
}

extend的高效率体现在它没有调用superClass构造函数,因此避免了在subClass.prototype上面创建不必要,多余的属性. 于此同时,原型链还能保持不变; 因此还能正常使用 instanceofisPrototypeOf() 方法。

js 原型相关知识点总结相关推荐

  1. 一文读懂JS继承相关知识点

    一文读懂JS继承相关知识点 Javascript 面向对象编程(一):封装 一. 生成实例对象的原始模式 二. 原始模式的改进 三. 构造函数模式 四.构造函数模式的问题 五. Prototype模式 ...

  2. JS事件相关知识点整理

    JS事件相关知识点整理 JS事件的驱动机制 常见JS事件 点击事件---onclick 焦点事件 获取焦点事件---onfocus 失去焦点事件----onblur 域内容改变事件---onchang ...

  3. js正则相关知识点专题

    1.JS正则表达式一条龙讲解(从原理和语法到JS正则)  //www.jb51.net/article/110516.htm 2.教你轻松记住JS正则表达式 //www.jb51.net/articl ...

  4. js 闭包及其相关知识点理解

    本文结合个人学习及实践,对闭包及相关知识点进行总结记录,欢迎读者提出任何不足之处 一.js变量 二.作用域(scope) 三.[[scope]] 和 scope chain 四.作用域(scope)和 ...

  5. html属性 id去重,JS相关知识点总结

    一.获取元素方法 1.document.getElementById("元素id号"); 可以使用内置对象document上的getElementById方法来获取页面上设置了id ...

  6. GAN相关知识点 - 纳什均衡、模型崩塌、WGAN原理、EM 距离、JS 散度等

    GAN相关知识点 - 纳什均衡.模型崩塌.WGAN原理.EM 距离.JS 散度等 一.纳什均衡 二.GAN 训练难题 三.WGAN 原理 一.纳什均衡 现在我们从理论层面进行分析,通过博弈学习的训练方 ...

  7. JS面试相关问题整理

    标题 前言 参考 JS基本数据类型和引用数据类型 什么是堆?什么是栈?它们之间有什么区别和联系? 内部属性 [[Class]] 是什么? 介绍 js 有哪些内置对象? null 和 undefined ...

  8. labview csv文件处理_LabVIEW 相关知识点分类汇总

    题图照片:Photo by Pixabay from Pexels 为了在知乎和小伙伴们一起更好进行LabVIEW相关知识与技术的讨论,特此制作了本知识点分类汇总供大家参考,分类采用先分块后分层的分而 ...

  9. 详解JS原型链与继承

    详解JS原型链与继承 JavaScript 目录 摘自JavaScript高级程序设计: 概念 确定原型和实例的关系 原型链的问题 借用构造函数 组合继承 原型继承 寄生式继承 寄生组合式继承 new ...

最新文章

  1. hibernate fetch使用
  2. oracle4045,ORACLE 错误 4045
  3. Linux Lernel Panic 报错解决思路
  4. Java数据结构和算法:线性表
  5. mysql 创建唯一索引_Mysql创建索引
  6. css 宽高最大值最小值 0303
  7. jfinal结合freemarker,页面使用$符获取属性值报错原因解决
  8. Java线程安全策略
  9. 知到大学生创业基础(上海理工大学)期末考试试题汇总(含答案)
  10. Google最新算法 - 人肉搜索引擎
  11. Mysql 不包含某个字符
  12. 卸载抖音和微博的一天……
  13. Redis-Lua脚本(集成SpringBoot工程)
  14. EXCEL基本操作技巧
  15. 【娱乐】Android实现监听通话、发送短信
  16. 战神引擎架设问题总结
  17. 发布一个iPhone版“远程桌面”
  18. 手机/平板上如何进行网页过滤
  19. JSTL标签不起作用,JSTL标签直接输出表达式
  20. 网页vnc工具NoVnc

热门文章

  1. J2EE高级软件工程师面试题集
  2. 华为:研发协同,为什么说效率是第一重要的事儿?
  3. 程序员的浪漫:三十行代码实现用她的名字作幅画
  4. Perl匹配之\w \s \d \b
  5. 揭秘Apple苹果在中国邮件营销这几年
  6. magento 2 Send Tracking Information 发送跟踪信息 无法发送邮件
  7. 【Python】爬虫(Xpath):批量爬取彼岸图网4K图(非真正4K)
  8. Jenkins 持续集成环境快捷部署
  9. nginx 结合php 实现高级配置详解
  10. ChatGPT检测器(Detector)