JS高级部分精讲
-
- 语法说明
- 基础总结
-
- 数据类型
-
- 分类
- 判断
- 问题
-
- undefined 与null的区别?
- 什么时候给变量赋值为null呢?
- 严格区别变量类型与数据类型?
- 数据、变量、内存
-
- 数据
- 变量
- 内存
- 关系
- 问题
-
- var a = xxx, a内存中到底保存的是什么?
- 在JS调用函数时传递变量参数时,是值传递还是引用传递?
- JS引擎如何管理内存?
- 对象
-
- 什么是对象?
- 为什么要用对象?
- 对象的组成?
- 如何操作内部属性(方法)?
- 问题:什么时候必须使用[ '属性名']的方式?
- 函数
-
- 什么是函数?
- 为什么要用函数?
- 如何定义函数?
- 如何调用(执行)函数?
- 回调函数?
- IIFE?
- 函数中的this?
- 函数高级
-
- 原型与原型链
-
- 构造函数、实例对象、原型对象
- 显式原型与隐式原型
- 原型链(隐式原型链)
- 区分读取属性值和设置属性值
- instanceof判断机制
- 执行上下文与执行上下文栈
-
- 变量提升和函数提升
- 全局执行上下文和函数执行上下文
- 作用域与作用域链
-
- 作用域链查找规则
- 作用域与执行上下文区别
- 闭包
-
- 什么是闭包?
- 产生闭包的条件?
- 闭包的作用与应用?
- 内存溢出与内存泄漏
- 对象高级
-
- 对象的创建模式
- 继承模式
- 线程事件
-
- 浏览器内核模块组成
- js线程
- 定时器问题:
- 事件处理机制
- H5 Web Workers
本篇文章主要包含四大部分:基础总结、函数高级、对象高级、线程事件。
语法说明 在下面2种情况下不加分号会有问题:
- 小括号开头的前一条语句
文章图片
- 中方括号开头的前一条语句
文章图片
除了上面两种情况,JS每一行是可以不加分号的!
基础总结 基础总结部分包含:数据类型,变量,内存,对象,函数。
数据类型
分类
- 基本(值)类型:
- Number -------- 任意数值
- String -------- 任意字符串
- Boolean -------- true/false
- undefined -------- undefined
- null -------- null
- 对象(引用)类型:
- Object -------- 任意对象
- Array -------- 一种特殊的对象(数组下标 内部数据有序)
- Function -------- 一种特殊的对象(可以执行)
- typeof:返回的是表示数据类型的字符串。
文章图片
- instanceof:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。(在后续原型链的讲解上会深入探讨)
文章图片
- ===:严格相等,即不仅值相等,还要类型相等。
文章图片
“==”运算符会在进行相等比较之前先进行必要值的类型转换,先把值转换为一样的类型再进行相等比较。就算比较的值的类型不相同,也可以通过强制转换类型成一样的,不会发生错误。
而“===”运算符,它不会执行类型转换,因此如果两个值不是相同类型,那么当比较时,它将返回false。如果比较两个变量,它们的类型彼此不兼容,则会产生编译错误。
“===”运算符首先比较两边的变量数据类型是否相等,其次比较两边的变量的数值是否相等;只有数据类型和数值都相等了,两个变量才相等。
什么时候给变量赋值为null呢? 答:初始赋值,表明将要赋值为对象;结束前,让对象成为垃圾对象(被垃圾回收器回收)。
严格区别变量类型与数据类型? 答:数据类型包含基本类型和对象类型;变量类型其实是变量内存值的类型,即基本类型和引用类型,基本类型表示保存就是基本类型的数据,引用类型表示保存的是地址值。
数据、变量、内存
数据 什么是数据?
数据是在内存中可读的、可传递的、保存了特定信息的事物。
变量 什么是变量?
变量是在程序运行过程中它的值是允许改变的量,其由变量名和变量值组成。
每个变量都对应的一块小内存,变量名用来查找对应的内存, 变量值就是内存中保存的数据。
内存 什么是内存?
内存是内存条通电后产生的存储空间(临时的)。
内存产生和死亡:内存条(电路版)>>通电>>产生内存空间>>存储数据>>处理数据>>断电>>内存空间和数据都消失。
一块内存包含2个方面的数据:内部存储的数据,地址值数据。
关系 内存,数据, 变量三者之间的关系?
内存是容器, 用来存储不同数据;变量是内存的标识, 通过变量我们可以操作(读/写)内存中的数据。
问题 var a = xxx, a内存中到底保存的是什么? 答:有以下几种情况:
- xxx是基本数据,保存的就是这个数据。
- xxx是对象,保存的就是对象的地址值。
- xxx是一个变量,保存的就是xxx的内存内容( 可能是基本数据,也可能是地址值)。
- 理解1:都是值(基本/地址值)传递;
- 理解2:可能是值传递, 也可能是引用传递(地址值);
- 分配小内存空间,得到它的使用权;
- 存储数据,可以反复进行操作;
- 释放小内存空间;
- 局部变量:函数执行完自动释放;
- 对象:成为垃圾对象==>垃圾回收器回收;
什么是对象? 一个对象代表现实中的一个事物。
- 多个数据(属性)的集合
- 用来保存多个数据(属性)的容器
对象的组成?
- 属性组成:
- 属性名 : 字符串(标识)
- 属性值 : 任意类型
- 属性的分类:
- 一般 : 属性值不是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条语句的封装体;
- 只有函数类型的数据是可以执行的, 其它的都不可以;
- 提高复用性;
- 便于阅读交流;
- 函数声明
function fn(){ }
- 表达式
var f=function fn(){}
文章图片
注意函数也是对象:
- 函数有属性: prototype;
- 函数有方法: call()/apply();
- 可以添加新的属性/方法;
文章图片
2.obj.test():通过对象调用;
3.new test(): new调用;
4.test.call/apply(obj):临时让test 成为bj的方法进行调用;
我们首先要理解一个概念“类”,所谓类,指的是对象的模版,对象就是类的实例。
对象是单个实物的抽象,所以通常需要一个模版,表示某一类实物的共同特征,然后对象根据这个模版生成,这个过程就叫做对象实例化。
但是在JS中是没有“类”这个概念的,而是用构造函数来作为对象模版的,所谓构造函数,指的就是专门生成对象的函数。
回调函数?
- 什么函数才是回调函数?
- 你定义的
- 你没有调用
- 但它最终执行了(在一定条件下或某个时刻)
- 常用的回调函数
- dom事件回调函数
- 定时器回调函数
- ajax请求回调函数
- 生命周期回调函数
- 全称: Immediately-Invoked Function Expression
- 作用:隐藏实现; 不会污染外部(全局)命名空间;
- 应用:用它来编码js模块;
- 所有函数内部都有一个变量this;
- 它的值是调用函数的当前对象;
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
- 原型对象即为当前实例对象的父对象。
原型链(隐式原型链)
文章图片
原型链: (隐式原型链)主要用于查找属性或者方法!
文章图片
- 所有的实例对象都有__proto__属性, 它指向的就是原型对象。
- 这样通过__proto__属性就形成了一个链的结构---->原型链。
- 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找:先在自身属性中查找,找到返回; 如果没有,再沿着_ proto_ 这条链向上查找,找到返回; 如果最终没找到,返回undefined。
文章图片
区分读取属性值和设置属性值 1、读取对象的属性值时:会自动到原型链中查找。
2、设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值。
3、方法一般定义在原型中,属性一般通过构造函数定义在对象本身上。
文章图片
instanceof判断机制 instanceof 是如何判断的?
- 表达式: A instanceof B。
- 如果B函数的显式原型对象在A对象的原型链上,返回true, 否则返回false。
变量提升和函数提升 1.变量声明提升?
- 通过var定义(声明)的变量,在定义语句之前就可以访问到。
- 值: undefined。(而不是报错)
- 通过function声明的函数,在之前就可以直接调用。
- 值:函数定义(对象)。
3.问题:变量提升和函数提升是如何产生的?
- 这就是我们接下来要讨论的执行上下文与执行上下文栈。
- 执行上下文与执行上下文栈:
- 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性。
- 执行上下文栈: 用来管理产生的多个执行上下文。
- 分类:
- 全局: window。
- 函数: 对程序员来说是透明的。
- 生命周期:
- 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡。
- 函数 : 调用函数时产生, 函数执行完时死亡。
文章图片
- 包含哪些属性:
- 全局 :
- 用var定义的全局变量 ==>undefined。
- 使用function声明的函数 ===>function。
- this ===>window。
- 函数:
- 用var定义的局部变量 ==>undefined。
- 使用function声明的函数 ===>function。
- this ===> 调用函数的对象, 如果没有指定就是window 。
- 形参变量 ===>对应实参值。
- arguments ===>实参列表的伪数组。
- 全局 :
文章图片
- 执行上下文创建和初始化的过程:
- 全局:
- 在全局代码执行前最先创建一个全局执行上下文(window)。
- 收集一些全局变量, 并初始化。
- 将这些变量设置为window的属性。
- 函数:
- 在调用函数时, 在执行函数体之前先创建一个函数执行上下文。
- 收集一些局部变量, 并初始化。
- 将这些变量设置为执行上下文的属性。
- 全局:
文章图片
作用域与作用域链
原型链是用来查找属性方法的!!
作用域链是用来查找变量的!!
- 理解:
- 作用域: 一块代码区域, 在编码时就确定了, 不会再变化。
- 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量。
- 分类:
- 全局。
- 函数。
- 作用:
- 作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突。
- 作用域链: 查找变量。
文章图片
查找一个变量的查找规则:
1、在当前作用域下的执行上下文中查找对应的属性,如果有直接返回, 否则进入2;
2、在上一级作用域的执行上下文中查找对应的属性,如果有直接返回, 否则进入3;
3、再次执行2的相同操作,.直到全局作用域, 如果还找不到就抛出找不到的异常;
文章图片
作用域与执行上下文区别
- 区别作用域与执行上下文:
- 作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了。
- 执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失。
- 联系: 执行上下文环境是在对应的作用域中的。
文章图片
什么是闭包?
- 理解一:闭包是嵌套的内部函数(绝大部分人)
- 理解二:包含被引用变量(函数)的对象(极少数人)
通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性。
产生闭包的条件? 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
- 函数嵌套。
- 内部函数引用了外部函数的数据(变量/函数)。
文章图片
产生闭包的个数 = 执行外部函数的次数。
1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
2.死亡:在嵌套的内部函数成为拉圾对象时。
文章图片
闭包的作用与应用?
文章图片
- 作用:
- 延长局部变量的生命周期。
- 让函数外部能操作内部的局部变量。
文章图片
- 应用:
- 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为。
- 循环遍历加监听。
- JS框架(jQuery)大量使用了闭包。
文章图片
- 缺点:
- 变量占用内存的时间可能会过长。
- 可能导致内存泄露。
- 解决:
- 及时释放 : f = null; //让内部函数对象成为垃圾对象。
文章图片
内存溢出与内存泄漏 1.内存溢出
- 一种程序运行出现的错误。
- 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。
- 占用的内存没有及时释放。
- 内存泄露积累多了就容易导致内存溢出。
- 意外的全局变量。
- 没有及时清理的计时器或回调函数。
- 闭包。
对象的创建模式
文章图片
- Object构造函数模式
var obj = {}; obj.name = 'Tom' obj.setName = function(name){this.name=name}
文章图片
- 对象字面量模式
var obj = { name : 'Tom', setName : function(name){this.name = name} }
文章图片
文章图片
文章图片
- 构造函数模式
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()
文章图片
浏览器内核模块组成
文章图片
- 主线程:
- js引擎模块 : 负责js程序的编译与运行。
- html,css文档解析模块 : 负责页面文本的解析。
- DOM/CSS模块 : 负责dom/css在内存中的相关处理 。
- 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)。
- 分线程:
- 定时器模块 : 负责定时器的管理。
- DOM事件模块 : 负责事件的管理。
- 网络请求模块 : 负责Ajax请求。
- js是单线程执行的(回调函数也是在主线程)。
- H5提出了实现多线程的方案: Web Workers。
- 只能是主线程更新界面。
- 定时器并不真正完全定时。
- 如果在主线程执行了一个长时间的操作, 可能导致延时才处理。
- 代码分类:
- 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码。
- 回调执行代码: 处理回调逻辑。
- js引擎执行代码的基本流程:
- 初始化代码===>回调代码。
- 模型的2个重要组成部分:
- 事件管理模块。
- 回调队列。
- 模型的运转流程:
- 执行初始化代码, 将事件回调函数交给对应模块管理。
- 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中。
- 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行。
文章图片
文章图片
- 可以让js在分线程执行。
- Worker。
var worker = new Worker('worker.js'); worker.onMessage = function(event){event.data} : 用来接收另一个线程发送过来的数据的回调 worker.postMessage(data1) : 向另一个线程发送数据
文章图片
文章图片
- 问题:
- worker内代码不能操作DOM更新UI。
- 不是每个浏览器都支持这个新特性。
- 不能跨域加载JS。
文章图片
下面继续学习es6咯!