WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链


JS高级部分精讲

    • 语法说明
    • 基础总结
      • 数据类型
        • 分类
        • 判断
        • 问题
          • undefined 与null的区别?
          • 什么时候给变量赋值为null呢?
          • 严格区别变量类型与数据类型?
      • 数据、变量、内存
        • 数据
        • 变量
        • 内存
        • 关系
        • 问题
          • var a = xxx, a内存中到底保存的是什么?
          • 在JS调用函数时传递变量参数时,是值传递还是引用传递?
          • JS引擎如何管理内存?
      • 对象
        • 什么是对象?
        • 为什么要用对象?
        • 对象的组成?
        • 如何操作内部属性(方法)?
        • 问题:什么时候必须使用[ '属性名']的方式?
      • 函数
        • 什么是函数?
        • 为什么要用函数?
        • 如何定义函数?
        • 如何调用(执行)函数?
        • 回调函数?
        • IIFE?
        • 函数中的this?
    • 函数高级
      • 原型与原型链
        • 构造函数、实例对象、原型对象
        • 显式原型与隐式原型
        • 原型链(隐式原型链)
        • 区分读取属性值和设置属性值
        • instanceof判断机制
      • 执行上下文与执行上下文栈
        • 变量提升和函数提升
        • 全局执行上下文和函数执行上下文
      • 作用域与作用域链
        • 作用域链查找规则
        • 作用域与执行上下文区别
      • 闭包
        • 什么是闭包?
        • 产生闭包的条件?
        • 闭包的作用与应用?
        • 内存溢出与内存泄漏
    • 对象高级
      • 对象的创建模式
      • 继承模式
    • 线程事件
      • 浏览器内核模块组成
      • js线程
      • 定时器问题:
      • 事件处理机制
      • H5 Web Workers

本篇文章主要包含四大部分:基础总结、函数高级、对象高级、线程事件。
语法说明 在下面2种情况下不加分号会有问题:
  • 小括号开头的前一条语句
    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

  • 中方括号开头的前一条语句
    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

【WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链】解决办法:在行首加分号!
除了上面两种情况,JS每一行是可以不加分号的!
基础总结 基础总结部分包含:数据类型,变量,内存,对象,函数。
数据类型
分类
  • 基本(值)类型:
  • Number -------- 任意数值
  • String -------- 任意字符串
  • Boolean -------- true/false
  • undefined -------- undefined
  • null -------- null
  • 对象(引用)类型:
  • Object -------- 任意对象
  • Array -------- 一种特殊的对象(数组下标 内部数据有序)
  • Function -------- 一种特殊的对象(可以执行)
判断
  • typeof:返回的是表示数据类型的字符串。

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

  • instanceof:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。(在后续原型链的讲解上会深入探讨)

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

  • ===:严格相等,即不仅值相等,还要类型相等。

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

    “==”运算符会在进行相等比较之前先进行必要值的类型转换,先把值转换为一样的类型再进行相等比较。就算比较的值的类型不相同,也可以通过强制转换类型成一样的,不会发生错误。
    而“===”运算符,它不会执行类型转换,因此如果两个值不是相同类型,那么当比较时,它将返回false。如果比较两个变量,它们的类型彼此不兼容,则会产生编译错误。
    “===”运算符首先比较两边的变量数据类型是否相等,其次比较两边的变量的数值是否相等;只有数据类型和数值都相等了,两个变量才相等。
问题 undefined 与null的区别? 答:undefined代表定义未赋值;nulll定义并赋值了,只是值为null 。
什么时候给变量赋值为null呢? 答:初始赋值,表明将要赋值为对象;结束前,让对象成为垃圾对象(被垃圾回收器回收)。
严格区别变量类型与数据类型? 答:数据类型包含基本类型和对象类型;变量类型其实是变量内存值的类型,即基本类型和引用类型,基本类型表示保存就是基本类型的数据,引用类型表示保存的是地址值。
数据、变量、内存
数据 什么是数据?
数据是在内存中可读的、可传递的、保存了特定信息的事物。
变量 什么是变量?
变量是在程序运行过程中它的值是允许改变的量,其由变量名和变量值组成。
每个变量都对应的一块小内存,变量名用来查找对应的内存, 变量值就是内存中保存的数据。
内存 什么是内存?
内存是内存条通电后产生的存储空间(临时的)。
内存产生和死亡:内存条(电路版)>>通电>>产生内存空间>>存储数据>>处理数据>>断电>>内存空间和数据都消失。
一块内存包含2个方面的数据:内部存储的数据,地址值数据。
关系 内存,数据, 变量三者之间的关系?
内存是容器, 用来存储不同数据;变量是内存的标识, 通过变量我们可以操作(读/写)内存中的数据。
问题 var a = xxx, a内存中到底保存的是什么? 答:有以下几种情况:
  • xxx是基本数据,保存的就是这个数据。
  • xxx是对象,保存的就是对象的地址值。
  • xxx是一个变量,保存的就是xxx的内存内容( 可能是基本数据,也可能是地址值)。
在JS调用函数时传递变量参数时,是值传递还是引用传递? 答:有以下两种理解:
  • 理解1:都是值(基本/地址值)传递;
  • 理解2:可能是值传递, 也可能是引用传递(地址值);
JS引擎如何管理内存? 答:内存的生命周期如下:
  • 分配小内存空间,得到它的使用权;
  • 存储数据,可以反复进行操作;
  • 释放小内存空间;
释放内存有以下两种情况:
  • 局部变量:函数执行完自动释放;
  • 对象:成为垃圾对象==>垃圾回收器回收;
对象
什么是对象? 一个对象代表现实中的一个事物。
  • 多个数据(属性)的集合
  • 用来保存多个数据(属性)的容器
为什么要用对象? 统一管理多个数据。
对象的组成?
  • 属性组成:
    • 属性名 : 字符串(标识)
    • 属性值 : 任意类型
  • 属性的分类:
    • 一般 : 属性值不是function的属性 描述对象的状态
    • 方法 : 属性值是function的属性 描述对象的行为
  • 特别的对象:
    • 数组: 属性名是0,1,2,3之类的索引
    • 函数: 可以执行的
如何操作内部属性(方法)?
① .属性名; ② ['属性名']: 属性名有特殊字符/属性名是一个变量;

①.属性名:编码简单,有时不能用; ②['属性名']:编码麻烦,能通用;

问题:什么时候必须使用[ ‘属性名’]的方式? 1.属性名包含特殊字符: - 空格
p['content-type'] ='text/json'

2.属性名不确定
var propName = 'myAge' var value=https://www.it610.com/article/18 p[propName] = vaLue

函数
什么是函数?
  • 用来实现特定功能的n条语句的封装体;
  • 只有函数类型的数据是可以执行的, 其它的都不可以;
为什么要用函数?
  • 提高复用性;
  • 便于阅读交流;
如何定义函数?
  1. 函数声明
    function fn(){ }

  2. 表达式
    var f=function fn(){}

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

    注意函数也是对象:
    • 函数有属性: prototype;
    • 函数有方法: call()/apply();
    • 可以添加新的属性/方法;
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
如何调用(执行)函数? 1.test(): 直接调用;
2.obj.test():通过对象调用;
3.new test(): new调用;
4.test.call/apply(obj):临时让test 成为bj的方法进行调用;
我们首先要理解一个概念“类”,所谓类,指的是对象的模版,对象就是类的实例。
对象是单个实物的抽象,所以通常需要一个模版,表示某一类实物的共同特征,然后对象根据这个模版生成,这个过程就叫做对象实例化。
但是在JS中是没有“类”这个概念的,而是用构造函数来作为对象模版的,所谓构造函数,指的就是专门生成对象的函数。
回调函数?
  • 什么函数才是回调函数?
    • 你定义的
    • 你没有调用
    • 但它最终执行了(在一定条件下或某个时刻)
  • 常用的回调函数
    • dom事件回调函数
    • 定时器回调函数
    • ajax请求回调函数
    • 生命周期回调函数
IIFE?
  • 全称: Immediately-Invoked Function Expression
  • 作用:隐藏实现; 不会污染外部(全局)命名空间;
  • 应用:用它来编码js模块;
函数中的this? 1、this是什么?
  • 所有函数内部都有一个变量this;
  • 它的值是调用函数的当前对象;
任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window。
2、如何确定this的值?
  • test(): window
  • p.test(): p
  • new test():新创建的对象
  • p.call(obj): obj
函数高级 函数高级部分包含:原型与原型链、执行上下文、执行上下文栈、变量提升、函数提升、作用域、作用域链、闭包。
原型与原型链
首先,我们先区分三个概念,构造函数、实例对象、原型对象。
然后,我们再来回顾一个概念,函数也是对象,所以函数有属性和方法。
构造函数、实例对象、原型对象
  • 所有构造函数都有一个特别的属性:
    • prototype : 显式原型属性。
  • 所有实例对象都有一个特别的属性:
    • __proto__ : 隐式原型属性。
  • 所有原型对象在初始时都是一个object空实例对象:
    • 原型对象中有一个属性constructor,它指向函数对象。
显式原型与隐式原型 显式原型与隐式原型的关系:
  • 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象。
    相当于执行this.prototype={}

  • 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值。
    相当于执行this.__proto__=Fn.prototype

  • 原型对象即为当前实例对象的父对象。
通过给原型对象添加属性( 一般都是方法),这样函数的所有实例对象自动拥有原型中的属性(方法)。
原型链(隐式原型链) WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

原型链: (隐式原型链)主要用于查找属性或者方法!
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 所有的实例对象都有__proto__属性, 它指向的就是原型对象。
  • 这样通过__proto__属性就形成了一个链的结构---->原型链。
  • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找:先在自身属性中查找,找到返回; 如果没有,再沿着_ proto_ 这条链向上查找,找到返回; 如果最终没找到,返回undefined。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

区分读取属性值和设置属性值 1、读取对象的属性值时:会自动到原型链中查找。
2、设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值。
3、方法一般定义在原型中,属性一般通过构造函数定义在对象本身上。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

instanceof判断机制 instanceof 是如何判断的?
  • 表达式: A instanceof B。
  • 如果B函数的显式原型对象在A对象的原型链上,返回true, 否则返回false。
执行上下文与执行上下文栈
变量提升和函数提升 1.变量声明提升?
  • 通过var定义(声明)的变量,在定义语句之前就可以访问到。
  • 值: undefined。(而不是报错)
2.函数声明提升?
  • 通过function声明的函数,在之前就可以直接调用。
  • 值:函数定义(对象)。
先有变量提升,再有函数提升。
3.问题:变量提升和函数提升是如何产生的?
  • 这就是我们接下来要讨论的执行上下文与执行上下文栈。
全局执行上下文和函数执行上下文
  • 执行上下文与执行上下文栈:
    • 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性。
    • 执行上下文栈: 用来管理产生的多个执行上下文。
  • 分类:
    • 全局: window。
    • 函数: 对程序员来说是透明的。
  • 生命周期:
    • 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡。
    • 函数 : 调用函数时产生, 函数执行完时死亡。
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
  • 包含哪些属性:
    • 全局 :
      • 用var定义的全局变量 ==>undefined。
      • 使用function声明的函数 ===>function。
      • this ===>window。
    • 函数:
      • 用var定义的局部变量 ==>undefined。
      • 使用function声明的函数 ===>function。
      • this ===> 调用函数的对象, 如果没有指定就是window 。
      • 形参变量 ===>对应实参值。
      • arguments ===>实参列表的伪数组。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 执行上下文创建和初始化的过程:
    • 全局:
      • 在全局代码执行前最先创建一个全局执行上下文(window)。
      • 收集一些全局变量, 并初始化。
      • 将这些变量设置为window的属性。
    • 函数:
      • 在调用函数时, 在执行函数体之前先创建一个函数执行上下文。
      • 收集一些局部变量, 并初始化。
      • 将这些变量设置为执行上下文的属性。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

作用域与作用域链
原型链是用来查找属性方法的!!
作用域链是用来查找变量的!!
  • 理解:
    • 作用域: 一块代码区域, 在编码时就确定了, 不会再变化。
    • 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量。
  • 分类:
    • 全局。
    • 函数。
  • 作用:
    • 作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突。
    • 作用域链: 查找变量。
作用域链查找规则 WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

查找一个变量的查找规则:
1、在当前作用域下的执行上下文中查找对应的属性,如果有直接返回, 否则进入2;
2、在上一级作用域的执行上下文中查找对应的属性,如果有直接返回, 否则进入3;
3、再次执行2的相同操作,.直到全局作用域, 如果还找不到就抛出找不到的异常;
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

作用域与执行上下文区别
  • 区别作用域与执行上下文:
    • 作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了。
    • 执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失。
    • 联系: 执行上下文环境是在对应的作用域中的。
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
闭包
什么是闭包?
  • 理解一:闭包是嵌套的内部函数(绝大部分人)
  • 理解二:包含被引用变量(函数)的对象(极少数人)
注意:闭包存在于嵌套的内部函数中。
通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性。
产生闭包的条件? 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
  • 函数嵌套。
  • 内部函数引用了外部函数的数据(变量/函数)。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

产生闭包的个数 = 执行外部函数的次数。
1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
2.死亡:在嵌套的内部函数成为拉圾对象时。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

闭包的作用与应用? WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 作用:
    • 延长局部变量的生命周期。
    • 让函数外部能操作内部的局部变量。
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
  • 应用:
    • 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为。
    • 循环遍历加监听。
    • JS框架(jQuery)大量使用了闭包。
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
  • 缺点:
    • 变量占用内存的时间可能会过长。
    • 可能导致内存泄露。
    • 解决:
      • 及时释放 : f = null; //让内部函数对象成为垃圾对象。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

内存溢出与内存泄漏 1.内存溢出
  • 一种程序运行出现的错误。
  • 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。
2.内存泄露
  • 占用的内存没有及时释放。
  • 内存泄露积累多了就容易导致内存溢出。
常见的内存泄露:
  • 意外的全局变量。
  • 没有及时清理的计时器或回调函数。
  • 闭包。
对象高级 对象高级部分包含:对象的多种创建模式、对象的继承模式。
对象的创建模式
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • Object构造函数模式
    var obj = {}; obj.name = 'Tom' obj.setName = function(name){this.name=name}

WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 对象字面量模式
    var obj = { name : 'Tom', setName : function(name){this.name = name} }

WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 构造函数模式
    function Person(name, age) { this.name = name; this.age = age; this.setName = function(name){this.name=name; }; } new Person('tom', 12);

WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 构造函数+原型的组合模式
    function Person(name, age) { this.name = name; this.age = age; } Person.prototype.setName = function(name){this.name=name; }; new Person('tom', 12);

继承模式
  • 原型链继承 : 得到方法
    function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子类型的原型指向父类型实例 Child.prototype.constructor = Child var child = new Child(); //有test()

  • 借用构造函数 : 得到属性
    function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx); //借用构造函数this.Parent(xxx) } var child = new Child('a', 'b'); //child.xxx为'a', 但child没有test()

  • 组合
    function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx); //借用构造函数this.Parent(xxx) } Child.prototype = new Parent(); //得到test() var child = new Child(); //child.xxx为'a', 也有test()

线程事件 线程事件部分包含:JavaScript事件循环机制。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

浏览器内核模块组成
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 主线程:
    • js引擎模块 : 负责js程序的编译与运行。
    • html,css文档解析模块 : 负责页面文本的解析。
    • DOM/CSS模块 : 负责dom/css在内存中的相关处理 。
    • 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)。
  • 分线程:
    • 定时器模块 : 负责定时器的管理。
    • DOM事件模块 : 负责事件的管理。
    • 网络请求模块 : 负责Ajax请求。
js线程
  • js是单线程执行的(回调函数也是在主线程)。
  • H5提出了实现多线程的方案: Web Workers。
  • 只能是主线程更新界面。
定时器问题:
  • 定时器并不真正完全定时。
  • 如果在主线程执行了一个长时间的操作, 可能导致延时才处理。
事件处理机制
  • 代码分类:
    • 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码。
    • 回调执行代码: 处理回调逻辑。
  • js引擎执行代码的基本流程:
    • 初始化代码===>回调代码。
  • 模型的2个重要组成部分:
    • 事件管理模块。
    • 回调队列。
  • 模型的运转流程:
    • 执行初始化代码, 将事件回调函数交给对应模块管理。
    • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中。
    • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行。
      WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
      文章图片
H5 Web Workers
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

  • 可以让js在分线程执行。
  • Worker。
    var worker = new Worker('worker.js'); worker.onMessage = function(event){event.data} : 用来接收另一个线程发送过来的数据的回调 worker.postMessage(data1) : 向另一个线程发送数据

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

    WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
    文章图片

  • 问题:
    • worker内代码不能操作DOM更新UI。
    • 不是每个浏览器都支持这个新特性。
    • 不能跨域加载JS。
WEB前端|JS高级部分精讲 —— 一文带你搞懂原型链
文章图片

下面继续学习es6咯!

    推荐阅读