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

JS高级部分精讲

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

本篇文章主要包含四大部分:基础总结、函数高级、对象高级、线程事件。

语法说明

在下面2种情况下不加分号会有问题:

  • 小括号开头的前一条语句
    JS高级部分精讲 —— 一文带你搞懂原型链_第1张图片

  • 中方括号开头的前一条语句
    JS高级部分精讲 —— 一文带你搞懂原型链_第2张图片

解决办法:在行首加分号!

除了上面两种情况,JS每一行是可以不加分号的!

基础总结

基础总结部分包含:数据类型,变量,内存,对象,函数。

数据类型

分类

  • 基本(值)类型:

  • Number -------- 任意数值

  • String -------- 任意字符串

  • Boolean -------- true/false

  • undefined -------- undefined

  • null -------- null

  • 对象(引用)类型:

  • Object -------- 任意对象

  • Array -------- 一种特殊的对象(数组下标 内部数据有序)

  • Function -------- 一种特殊的对象(可以执行)

判断

  • typeof:返回的是表示数据类型的字符串

    
    

    JS高级部分精讲 —— 一文带你搞懂原型链_第3张图片

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

    
    

    JS高级部分精讲 —— 一文带你搞懂原型链_第4张图片

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

    
    

    JS高级部分精讲 —— 一文带你搞懂原型链_第5张图片

    “==”运算符会在进行相等比较之前先进行必要值的类型转换,先把值转换为一样的类型再进行相等比较。就算比较的值的类型不相同,也可以通过强制转换类型成一样的,不会发生错误。

    而“===”运算符,它不会执行类型转换,因此如果两个值不是相同类型,那么当比较时,它将返回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=18
p[propName] = vaLue

函数

什么是函数?

  • 用来实现特定功能的n条语句的封装体;
  • 只有函数类型的数据是可以执行的, 其它的都不可以;

为什么要用函数?

  • 提高复用性;
  • 便于阅读交流;

如何定义函数?

  1. 函数声明

    function fn(){
    }
    
  2. 表达式

    var f=function fn(){}
    

    JS高级部分精讲 —— 一文带你搞懂原型链_第6张图片

    注意函数也是对象:

    • 函数有属性: prototype;
    • 函数有方法: call()/apply();
    • 可以添加新的属性/方法;
      JS高级部分精讲 —— 一文带你搞懂原型链_第7张图片

如何调用(执行)函数?

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
    
  • 原型对象即为当前实例对象的父对象。

通过给原型对象添加属性( 一般都是方法),这样函数的所有实例对象自动拥有原型中的属性(方法)。

原型链(隐式原型链)

JS高级部分精讲 —— 一文带你搞懂原型链_第8张图片

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

  • 所有的实例对象都有__proto__属性, 它指向的就是原型对象。
  • 这样通过__proto__属性就形成了一个链的结构---->原型链。
  • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找:先在自身属性中查找,找到返回;如果没有,再沿着_ proto_ 这条链向上查找,找到返回;如果最终没找到,返回undefined。

JS高级部分精讲 —— 一文带你搞懂原型链_第10张图片

区分读取属性值和设置属性值

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

instanceof判断机制

instanceof 是如何判断的?

  • 表达式: A instanceof B。
  • 如果B函数的显式原型对象在A对象的原型链上,返回true, 否则返回false。

执行上下文与执行上下文栈

变量提升和函数提升

1.变量声明提升?

  • 通过var定义(声明)的变量,在定义语句之前就可以访问到。
  • 值: undefined。(而不是报错)

2.函数声明提升?

  • 通过function声明的函数,在之前就可以直接调用。
  • 值:函数定义(对象)。

先有变量提升,再有函数提升。

3.问题:变量提升和函数提升是如何产生的?

  • 这就是我们接下来要讨论的执行上下文与执行上下文栈。

全局执行上下文和函数执行上下文

  • 执行上下文与执行上下文栈:

    • 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性。
    • 执行上下文栈: 用来管理产生的多个执行上下文。
  • 分类:

    • 全局: window。
    • 函数: 对程序员来说是透明的。
  • 生命周期:

    • 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡。
    • 函数 : 调用函数时产生, 函数执行完时死亡。
      JS高级部分精讲 —— 一文带你搞懂原型链_第12张图片
  • 包含哪些属性:

    • 全局 :
      • 用var定义的全局变量 ==>undefined。
      • 使用function声明的函数 ===>function。
      • this ===>window。
    • 函数:
      • 用var定义的局部变量 ==>undefined。
      • 使用function声明的函数 ===>function。
      • this ===> 调用函数的对象, 如果没有指定就是window 。
      • 形参变量 ===>对应实参值。
      • arguments ===>实参列表的伪数组。

JS高级部分精讲 —— 一文带你搞懂原型链_第13张图片

  • 执行上下文创建和初始化的过程:
    • 全局:
      • 在全局代码执行前最先创建一个全局执行上下文(window)。
      • 收集一些全局变量, 并初始化。
      • 将这些变量设置为window的属性。
    • 函数:
      • 在调用函数时, 在执行函数体之前先创建一个函数执行上下文。
      • 收集一些局部变量, 并初始化。
      • 将这些变量设置为执行上下文的属性。

JS高级部分精讲 —— 一文带你搞懂原型链_第14张图片

作用域与作用域链

原型链是用来查找属性方法的!!

作用域链是用来查找变量的!!

  • 理解:
    • 作用域: 一块代码区域, 在编码时就确定了, 不会再变化。
    • 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量。
  • 分类:
    • 全局。
    • 函数。
  • 作用:
    • 作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突。
    • 作用域链: 查找变量。

作用域链查找规则

JS高级部分精讲 —— 一文带你搞懂原型链_第15张图片

查找一个变量的查找规则:

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

作用域与执行上下文区别

  • 区别作用域与执行上下文:
    • 作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了。
    • 执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失。
    • 联系: 执行上下文环境是在对应的作用域中的。
      JS高级部分精讲 —— 一文带你搞懂原型链_第17张图片

闭包

什么是闭包?

  • 理解一:闭包是嵌套的内部函数(绝大部分人)
  • 理解二:包含被引用变量(函数)的对象(极少数人)

注意:闭包存在于嵌套的内部函数中。

通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性。

产生闭包的条件?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。

  • 函数嵌套。
  • 内部函数引用了外部函数的数据(变量/函数)。

在这里插入图片描述

产生闭包的个数 = 执行外部函数的次数。

1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
2.死亡:在嵌套的内部函数成为拉圾对象时。

在这里插入图片描述

闭包的作用与应用?

JS高级部分精讲 —— 一文带你搞懂原型链_第18张图片

  • 作用:

    • 延长局部变量的生命周期。
    • 让函数外部能操作内部的局部变量。
      JS高级部分精讲 —— 一文带你搞懂原型链_第19张图片
  • 应用:

    • 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为。
    • 循环遍历加监听。
    • JS框架(jQuery)大量使用了闭包。
      JS高级部分精讲 —— 一文带你搞懂原型链_第20张图片
  • 缺点:

    • 变量占用内存的时间可能会过长。
    • 可能导致内存泄露。
    • 解决:
      • 及时释放 : f = null; //让内部函数对象成为垃圾对象。

JS高级部分精讲 —— 一文带你搞懂原型链_第21张图片

内存溢出与内存泄漏

1.内存溢出

  • 一种程序运行出现的错误。
  • 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。

2.内存泄露

  • 占用的内存没有及时释放。
  • 内存泄露积累多了就容易导致内存溢出。

常见的内存泄露:

  • 意外的全局变量。
  • 没有及时清理的计时器或回调函数。
  • 闭包。

对象高级

对象高级部分包含:对象的多种创建模式、对象的继承模式。

对象的创建模式

JS高级部分精讲 —— 一文带你搞懂原型链_第22张图片

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

JS高级部分精讲 —— 一文带你搞懂原型链_第23张图片

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

JS高级部分精讲 —— 一文带你搞懂原型链_第24张图片
JS高级部分精讲 —— 一文带你搞懂原型链_第25张图片
JS高级部分精讲 —— 一文带你搞懂原型链_第26张图片

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

在这里插入图片描述

  • 构造函数+原型的组合模式
    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事件循环机制。
JS高级部分精讲 —— 一文带你搞懂原型链_第27张图片

浏览器内核模块组成

JS高级部分精讲 —— 一文带你搞懂原型链_第28张图片

  • 主线程:
    • js引擎模块 : 负责js程序的编译与运行。
    • html,css文档解析模块 : 负责页面文本的解析。
    • DOM/CSS模块 : 负责dom/css在内存中的相关处理 。
    • 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)。
  • 分线程:
    • 定时器模块 : 负责定时器的管理。
    • DOM事件模块 : 负责事件的管理。
    • 网络请求模块 : 负责Ajax请求。

js线程

  • js是单线程执行的(回调函数也是在主线程)。
  • H5提出了实现多线程的方案: Web Workers。
  • 只能是主线程更新界面。

定时器问题:

  • 定时器并不真正完全定时。
  • 如果在主线程执行了一个长时间的操作, 可能导致延时才处理。

事件处理机制

  • 代码分类:
    • 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码。
    • 回调执行代码: 处理回调逻辑。
  • js引擎执行代码的基本流程:
    • 初始化代码===>回调代码。
  • 模型的2个重要组成部分:
    • 事件管理模块。
    • 回调队列。
  • 模型的运转流程:
    • 执行初始化代码, 将事件回调函数交给对应模块管理。
    • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中。
    • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行。
      JS高级部分精讲 —— 一文带你搞懂原型链_第29张图片

H5 Web Workers

JS高级部分精讲 —— 一文带你搞懂原型链_第30张图片

  • 可以让js在分线程执行。

  • Worker。

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

    JS高级部分精讲 —— 一文带你搞懂原型链_第31张图片
    JS高级部分精讲 —— 一文带你搞懂原型链_第32张图片

  • 问题:

    • worker内代码不能操作DOM更新UI。
    • 不是每个浏览器都支持这个新特性。
    • 不能跨域加载JS。

在这里插入图片描述
下面继续学习es6咯!

你可能感兴趣的:(WEB前端,js高级,原型与原型链,闭包)