Javascript

目录



第一章 基础总结

1. 数据类型(字面量)

1.1 分类
  • 基本(值)类型
    • String => 任意字符串
    • Number => 任意的数字
    • Boolean => true/false
    • Null => null
    • Undefined => undefined
  • 对象(引用)类型
    • Object => 任意对象
    • Function => 一种特别的对象(可以执行)
    • Array => 一种特别的对象(数值下标,内部数据为有序的)
1.2 判断
  • typeof
    • 可以判断 undefined、数值、字符串、布尔值、function
    • 不能判断 Null 与 Object Object 与 Array
  • instanceof
    • 判断对象的具体类型
  • ===
    • 可以判断 undefined、null
1.3 相关问题

问题 1:undefined 与 null 的区别?

  1. undefined 代表定义未赋值、
  2. null 代表了定义了并已经赋值

问题 2:什么时候给变量赋值为 null?

  1. 初始赋值 => 表明将要赋值
  2. 结束赋值 => 让对象被回收

问题 3:严格要求变量的类型与数据类型?

  1. 数据的类型
    • 数据类型
    • 对象类型
  2. 引用的类型(变量内存值的类型)
    • 基本类型:保存就是基本类型的数据
    • 引用类型:保存的是地址值

2. 数据·数据·内存

2.1 类型转换
  • 转换成 String 类型
    • toString()
  • 转换成 Number 类型
    • parseInt()
    • parseFloat()
  • 转换成 Boolean 类型
    • 数字–>布尔
      • 除了 0 和 NaN,其余都是 true
    • 字符串–>布尔
      • 除了空串,其余都是 true
    • null 和 undefined 都会转换成 false
    • 对象转换成 true
2.2 运算符
  • 布尔值时
    • ! (非)
    • && (与) --> 一个为 false,全为 false
    • || (或) --> 一个为 true, 全为 true
  • 非布尔值时
    • && (与)
      • 如果第一个值为 true,则必然返回第二个值
      • 如果第一个值为 false,则直接返回第一个值
    • || (或)
      • 如果第一个值为 true,则直接返回第一个值
      • 如果第一个值为 false,则直接返回第二个值
  • 优先级: || 小于 &&
2.3 Unicode 编码
  • 控制台可以输出 \u000
  • 网页中可以输出 ⚀==> ⚀
2.4 条件运算符(三元运算符)
  • 条件表达式?语法 1:语法 2
    • [条件为 true 输出语法 1,条件为 false 输出语法 2]
2.5 流程控制语句
  • 分类

    • 条件判断语句
    • 条件分支语法
    • 村换语句
  • break 终止当次循环 终止内部循环
  • continue 跳过当前循环 继续从起始位置循环
  • 数值

Number.MAX_VALUE 表示数值的最大值
Number.MIN_VALUE 表示数值的最小值

2.6 相关问题

问题 1:什么是数据?

  1. 存储在内存中代表特定信息的(本质上就是 0100)
  2. 数据的特点:
    • 可传递
    • 可运算
  3. 一切皆数据
  4. 内存中所有操作的目标:数据
    • 算术运算
    • 逻辑运算
    • 赋值
    • 运行函数

问题 2:什么是内存?

  1. 内存条通电后产生的可存储数据的空间(临时的)
  2. 内存产生和死亡:
    • 内存条(电路板) ==> 通电 ==> 产生内存空间 ==> 存储数据 ==> 处理数据 ==> 断电 ==> 内存空间的数据消失
  3. 一块小内存的 2 个数据
    • 内存存储的数据
    • 地址值
  4. 内存分类
    • 栈: 全局变量/局部变量
    • 堆: 对象

问题 3:什么是变量?

  1. 可变化的量,由变量名和变量值组成
  2. 每个变量都有对应一个小内存
    • 变量名用来查找对应的内存
    • 变量值就是内存中保存的数据

问题 4:内存,数据,变量 三者之间的关系?

  1. 内存用来存储数据的空间
  2. 变量是内存的标识

问题 5: var a = xxx, a 内存中到底保存的是什么?

  1. xxx 是基本数据 --> 数据
  2. xxx 是一个对象 --> 对象的地址值
  3. xxx 是一个变量 --> xxx 的内容

问题 6: 关于引用变量赋值问题

  1. 2 个引用变量指向同一个对象
    • 通过一个变量修改对象内部数据
    • 另一个变量看到的是修改之后的数据
  2. 2 个引用变量指向同一个对象
    • 让其中一个引用对象指向另一个对象
    • 另一个引用变量依然指向前一个对象
// 例1
var obj1 = { name: "Tom" }
var obj2 = obj1
obj2.age = 12
console.log(obj1.age) // 12
function fn(obj) {
    obj.name = "A"
}
fn(obj1)
console.log(obj2.name) // A

// 例2
var a = { age: 12 }
var b = a
a = { name: "Bob", age: 13 }
b.age = 14
console.log(b.age, a.name, a.age) // 14 Bob 13
function fn2(obj) {
    obj = { age: 15 }
}
fn2(a)
console.log(a.age) //13

问题 7: 在 JS 调用函数时传递变量参数时, 是 值传递 还是 引用传递

  1. 都是值(基本/地址值)传递
  2. 可能是值传递 也可能是引用传递(地址值)
var a = 3
function fn(a) {
    a = a + 1
}
fn(a)
console.log(a) // 3
console.log(fn(a)) // 4

问题 8: JS 引擎如何管理内存?

  1. 内存生命周期

    • 分配小内存空间,得到使用权
    • 存储数据,可以反复进行操作
    • 释放小内存空间
  2. 释放内存

    • 局部变量: 函数执行完自动释放
    • 对象 : 成为垃圾对象 => 垃圾回收器回收

3. 对象

3.1 对象的分类
  1. 内建对象

    • 由 ES 标准定义的对象 在任何的 ES 的实现中都可以使用
    • 比如 Math String Number Function Object…
  2. 宿主对象

    • 由 JS 的运行环境提供的对象 目前来讲主要指浏览器提供的对象
    • 比如 BOM DOM
  3. 自定义对象

    • 由开发人员之间创建的对象
3.2 基本和引用数据类型
  1. 引用数据类型
    • Object
  2. 在 JS 中变量都是保存到栈内存中的
    • 基本数据类型的值基本在栈内存中存储
    • 值与值之间是独立存在的
  3. 对象是保存在堆内存中的
    • 每开辟一个新的对象时,在堆内存中开辟一个新的空间
    • 而变量保存的是对象的内存地(对象的引用),如果两个变量保存的是同一个变量的引用,当一个变量修改变量值,另一个也会变化
3. 对象的相关问题

问题 1:什么是对象?

  • 多个数据的封装体
  • 用来保存多个数据的容器
  • 一个对象代表现实中的一个事物

问题 2:为什么要用对象?

  • 统一管理多个数据

问题 3:对象的组成?

  • 属性: 属性名(字符串)和属性值(任意类型)组成
  • 方法: 一种特别的属性(属性值是函数)

问题 4:如何访问对象内部数据?

  • 属性名: 编码简单 有时不能用
  • [‘属性名’](属性值) :编码复杂 常用

问题 5: 什么时候必须使用[‘属性名’]的方式?

  • 属性名包含特殊字符: - 空格
  • 属性名不确定
var p = {}
// 特殊字符
p["content-type"] = "text/json"
console.log(p["content-type"])

// 属性名不确定
var propName = "myAge"
var value = 18
p[propName] = value
console.log(p[propName])

4. 函数

4.1 返回值的类型
  • 返回值可以是任意的数据类型
  • 也可以是一个对象
  • 也可以是一个函数
4.2 枚举对象
  1. for … in 语句:对象中有几个属性,循环体就会执行几次
  2. 每次执行时,会将对象中的一个属性的名字赋值给变量
for (var n in obj) {
    // 输出obj对象中的所有属性名
    console.log("属性名:" + n)

    // 输出obj对象中的所有属性值
    console.log("属性值:" + obj[n])
}
4.3 全局作用域
  • 作用域指一个变量的范围

  • 在 JS 中一共有两种作用域

    • 直接编写在 script 标签中的 JS 代码都在全局作用域
    • 全局作用域在页面打开时创建,在页面关闭时销毁
    • 在全局作用域中有一个全局对象 window
    • 它代表的是一个浏览器的窗口
    • 它由浏览器创建,可以直接使用
  • 在全局作用域中

    • 创建的变量都会作为 window 对象的属性保存
    • 创建的函数都会作为 window 对象的方法保存
    • 全局作用域中的变量都是全局变量
  • 在页面的任意部分都可以访问到

    作用域.jpg

4.4 IIFE
  1. 理解 (立即调用函数表达式)
    • 全称: Immediately-Invoked Function Expression
  2. 作用
    • 隐藏实现
    • 不会污染外部(全局)命名空间
    • 用来编写 Js 模块

注意:立即执行函数,往往只会执行一次

// ()()形成了立即调用函数
(function foo(){
	for(let i in bar){
		console.log(i)
	}
})()
4.5 声明提前
  1. 变量的声明提前
    • 使用 var 关键字的变量,会在所有的代码执行之前被声明
      • 但是如果声明变量时不适用 var 关键字,则变量不会被声明提前
  2. 函数的声明提前
    • 使用函数声明形式创建的函数 function 函数(){}
      • 它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数
    • 使用函数表达式创建的函数,不会被声明提前,所以不能声明前调用

注意:只有 var 才具有声明提前的性质,let, const 均不具有该性质

4.6 函数作用域
  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域被销毁
  • 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
  • 在函数作用域中可以访问到全局作用域的变量
    • 全局作用域中无法访问到函数作用域的变量
  • 当在函数作用域操作一个变量时,它会现在自身作用域中寻找 如果有直接调用
    • 如果没有则向上一级中寻找,直到找到为止如果都没有就会报错
  • 在函数中要访问全局变量可以使用 window 对象
4.7 函数的对象 call()/apply()
  • 都需要函数对象的方法,需要通过函数对象来调用
  • 在调用 call()和 apply()可以将一个对象指定为第一个参数
    • 此时这个对象将会成为函数执行的 this
  • call()方法可以将实参在对象之后依次传递
  • apply()方法需要将实参封装在一个数组中统一传递
function fun(a, b) {
    console.log("a =" + a)
    console.log("b =" + b)
    alert(this)
}
var obj = {
    name: "obj1",
    sayName: function () {
        alert(this.name)
    },
}
var obj2 = {
    name: "obj2",
}
fun.call() // 等同于 fun.apply() == fun()

// 指定参数
fun.call(obj) // this = obj
obj.sayName.apply(obj2) // obj2

fun.call(obj, 2, 3) // a = 2, b = 3
fun.call(obj, [2, 3]) // a = 2, b = 3
4.8 列表参数

在调用函数时,浏览器每次都会传递两个隐含参数

  1. 函数的上下对象 this

  2. 封装实参的对象 argument

    • argument 是一个类数组对象,也可以通过索引来操作数据 也可以获取长度
    • 在调用函数时,我们所传递的实参都会在 argument 中保存
    • 即使不定义形参,也可以通过 argument 来使用实参 (过于麻烦)
  3. 属性 callee

    • 这个属性对应一个函数对象,就是当前正在指向的函数的对象
4.9 构造函数
  • 构造函数就是一个普通的函数,创建函数和普通函数没有区别

  • 不同的是构造函数习惯上首字母大写

  • 构造函数和普通函数的区别就是调用方式的不同

    • 普通函数是直接调用
    • 构造函数则需要使用关键字 new 来调用
  • 使用同一个构造函数创建的对象,我们将这一类对象,称为构造函数的一个类

  • 所有的对象都是 Object 的后代

  • 构造函数的执行流程

    1. 立即创建一个新的对象
    2. 将新建的对象设置为函数中的 this,在新建的对象中可以使用 this 来引用新建的函数
    3. 逐行执行函数中的代码
    4. 将新建的对象作为返回值返回
function Person(name, age) {
    // alert(this);  // this 等同于 per
    this.name = name
    this.age = age
}
// 构造函数
var per = new Person("孙悟空", 18)
var per2 = new Person("猪八戒", 18)
4.10 函数中的 this

问题 1:this 是什么?

  • 任何函数本质上就是通过某个对象来调用的,如果没有指定都是 window
  • 所有函数内部都有一个变量 this
  • 它的值是调用函数的当前对象

问题 2:如何确定 this 的值?

  • test() : window
  • p.test(): p
  • new test(): 新创建的对象
  • p.call(obj): obj
function Person(color) {
    console.log(this)
    this.color = color
    this.getColor = function () {
        console.log(this)
        return this.color
    }
    this.setColor = function (this) {
        console.log(this)
        this.color = color
    }
}
Person("red") //this是谁? window

var p = new Person("yellow") //this是谁? p

p.getColor() //this是谁? p

var obj = {}
p.setColor.call(obj, "black") //this是谁? obj

var test = p.setColor
test() //this是谁? window

function fun1() {
    function fun2() {
        console.log(this)
    }
    fun2() //this是谁? window
}
fun1()
4.11 回调函数的相关问题

问题 1:什么函数才是回调函数?

  • 1). 你定义的
  • 2). 你没有调用
  • 3). 但最终执行了

问题 2:常见的回调函数?

  • dom 事件回调函数
  • 定时器回调函数
  • ajax 请求回调函数
  • 生命周期回调函数
4.12 函数的相关问题

问题 1:什么是函数?

  • 实现特定功能的 n 条语句的封装体
  • 只有函数时可以执行的 其他类型的数据不能执行

问题 2:为什么要用函数?

  • 提高代码复用
  • 便于阅读交流

问题 3:如何定义函数?

  • 函数声明
  • 表达式
// 函数声明
function fun1() {
    console.log("fun1()")
}

// 表达式
var fun2 = function () {
    console.log("fun2()")
}

问题 4:如何调用(执行)函数?

  • test(): 直接调用
  • obj.test(): 通过对象调用
  • new test(): new 调用
  • test.call/apply(obj): obj 调用

5. math 的常见方法

  1. 绝对值

    Math.abs()

  2. x 的 y 次方

    方法一:Math.pow(x, y)
    方法二:x**y

  3. x 的平方根

    Math.sqrt(x)

  4. 上舍入

    Math.ceil(1.3) // 2

  5. 下舍入

    Math.floor(1.8) // 1

  6. 四舍五入

    Math.round(1.3) // 1

  7. random 函数=> 随机生成 0-1 之间的随机数

    Math.random()

  8. x-y 之间的随机数

    Math.round(Math.random() * (y - x) + x)

6. 数组方法

  1. push()

    在数组末尾添加一个或多个元素

  2. pop()

    删除数组末尾的一个元素,该值并作为返回值返回

  3. unshift()

    在数组的头部添加一个或多个元素,并返回长度

  4. shift()

    删除末尾的一个元素,该值并作为返回值返回

  5. forEach(value,index,obj)【*ES5新增】

    变量整个数组
    value => 当前遍历的元素
    index => 当前遍历的元素的索引
    obj => 当前遍历的数值

  6. splice(x, y, z)

    可以删除、添加、替换
    x => 开始的位置
    y => 删除的个数
    z => 添加的元素(一个或多个)

  7. concat()

    可以合并两个或多个数组,并返回新的数组

  8. join()

    可以将数组转换为字符串,可以指定一个字符串作为连接符,不指定默认为 ‘,’ 作为连接符

  9. reverse()

    将数组按照倒序的方式排列(针对于纯数字),根据 unicode 编码

  10. sort()

    将数组按照正序的方式排列(针对于纯数字),根据 unicode 编码

  11. filter() 【*ES5新增】

    过滤整个数组,将满足条件的数字返回到一个新的数组中

  12. map() 【*ES5新增】

    遍历整个数组,可以对数组进行加减乘除操作

  13. reduce() 【*ES5新增】

    遍历整个数组,将所有的数字进行汇总,得到一个总和

const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
/* 方法 1 */
// filter过滤后得到新数组 [6, 7, 8, 9]
let nums2 = nums.filter(n => n > 5)
// map 遍历将数组中每个数字乘 2 ==> [12, 14, 16, 18]
let nums3 = nums2.map(n => n * 2)
// reduce 遍历数组将每个数字相加 (preValue 默认为 0) => 12 + 14 + 16 + 18 = 60
/*
let total = nums3.reduce(function(preValue, n){
	return perValue + n
}, 0)
*/
let total = nums3.reduce((preValue, n) => preValue + n)
/* 方法 2 */
let total = nums.filter(n => n > 5).map(n => n * 2).reduce((preValue, n) => preValue + n)
console.log(total) // total = 60
  1. indexOf() 【*ES5新增<该方法对大小写敏感!>】

    查找一个字符串中,第一次出现指定字符串的位置

  2. every() 【*ES5新增】

    判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true

  3. some() 【*ES5新增】

    判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true

7. BOM

  • 浏览器对象模型

  • BOM 可以使我们通过 JS 来操作浏览器

  • 在 BOM 中为我们提供一组对象,用来完成对浏览器的操作

  • BOM 对象

    1. Window

      • 代表的是整个浏览器的窗口
      • 同时 window 也是网页中的全局对象
    2. Navigator

      • 代表的当前浏览器的信息通过该对象可以来识别不同的浏览器
    3. Location

      • 代表当前浏览器的地址信息
      • 可以通过 Location 获取地址栏信息,或者跳转界面
    4. History

      • 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史
      • 由于隐私原因,该对象不能获取到具体的历史记录 只能操作浏览器向前或向后翻页
      • 而且该操作只在当次访问时有效
    5. Screen(相对较小)

      • 代表用户的屏幕的信息,通过对象可以获取用户的显示器的相关信息
  • 这些 BOM 对象在浏览器中都是 window 对象的属性保存

    • 可以通过 window 对象来使用,也可以直接使用

8. JSON

  • JS 中的对象只有 JS 自己认识 其他的语言都不认识

  • JSON 就是一个特殊格式的字符串,这个字符串可以被任意语言所识别

    • 并且可以转换为任意语言中的对象
    • JSON 在开发中主要用来数据的交互
  • JSON

    • JavaScript Object Notation JS 对象表示法
    • JSON 和 JS 对象的格式一样,只不过 JSON 字符串中的属性名必须加双引号
  1. JSON 分类:

    1. 对象 {}
    2. 数组 {}
  2. JSON 中允许的值:

    1. 字符串
    2. 数值
    3. 布尔值
    4. 空值(null)
    5. 对象
    6. 数组
//  将JSON字符串转换成JS中的对象
var json = '{"name": "孙悟空", "age": 18, "sex": "男"}'

// json --> js对象
// JSON.parse()
var o = JSON.parse()
console.log(o.name)

// js对象 ---> json
// JSON.stringify()
var obj = { name: "孙悟空", age: 18, sex: "男" }
var str = JSON.stringify()

第二章 函数高级

1. 闭包

1.1 认识闭包
  1. 含义(如何产生闭包?)
    • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就会产生闭包
  2. 理解(闭包到底是什么?)
    • 理解一: 闭包是嵌套的内部函数(绝大部分人)
    • 理解二: 包含被引用变量(函数)的对象(极少数人)
    • 注意: 闭包存在于嵌套的内部函数中
  3. 条件(产生闭包的条件?)
    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量/函数)
    • 执行外部函数
function fn1() {
    var a = 2
    var b = "abc"
    function fn2() {
        // 执行函数定义就会产生闭包(不用调用内部函数)
        console.log(a)
    }
    fn2()
}
fn1()
1.2 常见的闭包
  1. 将函数作为另一个函数的返回值
function fn1() {
    var a = 2
    function fn2() {
        a++
        console.log(a)
    }
    return fn2
}
var f = fn1()
f() // 3
f() // 4
  1. 将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
    setTimeout(function () {
        // 闭包包含msg 不包括time
        alert(msg)
    }, time)
}
showDelay("Tom", 2000)
1.3 闭包的作用
  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题 1:函数执行完后, 函数内部声明的局部变量是否还存在?

  • 一般情况下不存在, 只有存在于闭包里的变量才会存在

问题 2:在函数外部能直接访问函数内部的局部变量吗?

  • 不能, 但我们可以通过闭包让外部操作它
1.4 闭包的生命周期
  1. 产生: 在嵌套内部函数定义执行完时就产生了(不会再调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
function fn1() {
    // 此时闭包就已经产生了{函数提升,内部函数对象已经创建了}
    var a = 2
    function fn2() {
        a++
        console.log(a)
    }
    return fn2
}
var f = fn1()
f() // 3
f() // 4

f = null // 闭包死亡(包含闭包的函数对象成为垃圾对象)
1.5 闭包的应用
  • 具有特定功能的 js 文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包含 n 个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
1.6 闭包的缺点及解决
  1. 缺点
    • 函数执行完后, 函数内的局部变量没有释放,占用内存时间会变长
    • 容易造成内存泄露
  2. 解决
    • 能不用闭包就不用
    • 及时释放

2. 原型

2.1 认识原型
  1. 函数的 prototype 属性

    • 每个函数都有一个 prototype 属性
      • 默认指向一个 Object 空对象(即称为:原型对象)
    • 原型对象都有一个 constructor 属性
      • 默认指向函数对象
  2. 给原型对象添加属性(一般都是方法)

    • 作用:
      • 函数的所有实例对象自动拥有原型中的属性(方法)
function fun() {}
fun.prototype.test = function () {
    console.log("test()")
}
console.log(fun.prototype) // 默认向一个Object空对象(没有我们的属性)

// 原型对象都有一个constructor属性 默认指向函数对象
console.log(Date.prototype.constructor == Date)
console.log(fun.prototype.constructor == fun)

// 给原型对象添加属性(一般都是方法)==>实例对象可以访问
fun.prototype.test = function () {
    console.log("test()")
}
2.2 显示原型与隐式原型
  1. 每个函数 function 都有一个 prototype,即显式原型(属性)
  2. 每个实例对象都有一个__proto__,即隐式原型(属性)
  3. 对象的隐式原型的值为其对应构造函数的显示原型的值
  4. 内存的结构
  5. 总结:
    • 函数的 prototype 属性: 在定义函数时自动添加的
      • 默认值是一个空 Object 对象
    • 对象的__proto__属性: 创建对象时自动添加
      • 默认值是构造函数的 prototype 属性值
    • 程序员能直接操作显式原型,但不能直接操作隐式原型(ES6 之前)
// 定义构造函数
function Fn() {
    // 内部语句:this.prototype = {}
}

// 1. 每个函数function都有一个prototype, 即显式原型
console.log(Fn.prototype)

// 2. 每个实例对象都有一个__proto__, 即隐式原型
// 创建实例对象
var fn = new Fn() // 内部语句:this.__proto__ = Fn.prototype
console.log(fn.__proto__)

// 3. 对象的隐式原型的值为其对应构造函数的显示原型的值
console.log(Fn.prototype === fn.__proto__) //true

// 给原型添加方法
Fn.prototype.test = function () {
    console.log("test()")
}
fn.test()

原型.jpg

2.3 原型链
  1. 原型链

    • 访问一个对象的属性时
      • 先在自身属性中查找,找到返回
      • 如果没有,再沿着__proto__这条链向上查找,找到返回
      • 如果最终没找到,返回 undefined
    • 别名:隐式原型链
    • 作用:查找对象的属性(方法)

    原型链.jpg

  2. 构造函数/原型/实体对象的关系

    构造函数,原型,实例对象的关系.jpg

  3. 函数的显式原型指向的对象默认是空 Object 实例对象(但 Object 不满足)

  4. 所有函数都是 Function 的实例(包含 Function)

  5. Object 的原型对象是原型链尽头

2.4 原型链的属性
  1. 读取对象的属性值时: 会自动到原型链中查找
  2. 设置对象的属性值时: 不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
  3. 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
2.5 探索 instanceof
  1. instanceof 是如何判断的?
    • 表达式: A instanceof B
    • 如果 B 函数的显式原型对象是 A 对象的原型链上,返回 true,否则返回 false
  2. Function 是通过 new 自己产生的实例
// 案例 1
function Foo() {}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true

// 案例 2
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true

function Foo() {}
console.log(Object instanceof Foo) // false

探索instanceof案例2.jpg

3. 执行上下文和执行上下文栈

3.1 声明提前
  1. 变量声明提升
    • 通过 var 定义(声明)的变量,在定义语句之前就可以访问到
    • 值: undefined
  2. 函数声明提升
    • 通过 function 声明的函数,在之前就可以直接调用
    • 值: 函数定义(对象)
// 面试题
var a = 3
function fn() {
    console.log(a)
    var a = 4
}
fn() // undefined
3.2 执行上下文
  1. 代码分类(位置)

    • 全局代码
    • 函数(局部)代码
  2. 全局执行上下文

    • 在执行全局代码前将 window 确定为全局执行上下文
    • 对全局数据进行预处理
      • var 定义的全局变量==>undefined, 添加为 window 的属性
      • function 声明的全局函数==>赋值(fun), 添加为 window 的方法
      • this==>赋值(window)
    • 开始执行全局代码
  3. 函数执行上下文

    • 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的,存在于栈中)
    • 对局部数据进行预处理
      • 形参变量==>赋值(实参)==>添加为执行上下文的属性
      • arguments==>赋值(实参列表), 添加为执行上下文的属性
      • var 定义的局部变量==>undefined, 添加为执行上下文的属性
      • function 声明的函数==>赋值(fun), 添加为执行上下文的方法
      • this==>赋值(调用函数的对象)
    • 开始执行函数体代码
// 函数执行上下文
function fn(a1) {
    console.log(a1) // 2
    console.log(a2) // undefined
    a3() // a3()
    console.log(this) // window
    console.log(arguments) // 伪数组(2, 3)

    var a2 = 3
    function a3() {
        console.log("a3()")
    }
}
fn(2, 3)
3.3 执行上下文栈
  1. 在全局执行代码前, JS 引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
  3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
  4. 在当前函数执行完后, 将栈定的对象移除(出栈)
  5. 当所有的代码执行完后, 栈中只剩下 window
// 1.进入全局执行上下文
var a = 10
var bar = function (x) {
    var b = 5
    foo(x + b) // 3.进入foo执行上下文
}
var foo = function (y) {
    var c = 5
    console.log(a + c + y)
}
bar(10) // 进入bar函数执行上下文
3.4 相关面试题
  • 面试题 1: 依次输出什么?

    • global begin: undefined
    • foo() begin: 1
    • foo() begin: 2
    • foo() begin: 3
    • foo() end: 3
    • foo() end: 2
    • foo() end: 1
    • global end: 1
  • 面试题 2: 整个过程中产生几个执行上下文?

    • 5
console.log("global begin:" + i)
var i = 1
foo(1)
function foo(i) {
    if (i == 4) {
        return
    }
    console.log("foo() begin:" + i)
    foo(i + 1) // 递归调用
    console.log("foo() end:" + i)
}
console.log("global end:" + i)

4. 作用域

4.1 认识作用域
  1. 理解
    • 就是一块"地盘", 一个代码段所在的区域
    • 它就是静态的(相对于上下文对象), 在编写代码时就确定了
  2. 分类
    • 全局作用域
    • 函数作用域
    • 没有快作用域(ES6 有了==> let 创建的 )
  3. 作用
    • 隔离变量, 不同作用域下同名变量不会有冲突
4.2 作用域与执行上下文
  1. 区别 1
    • 全局作用域之外, 每个函数都会创建自己的作用域, 作用域在函数定义时就已经确定了。而不是在函数调用时
    • 全局执行上下文环境是在全局作用域确定之后,js 代码马上执行之前创建
    • 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建
  2. 区别 2
    • 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
    • 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放
  3. 联系
    • 执行上下文环境(对象)是从属于所在的作用域
    • 全局上下文环境==>全局作用域
    • 函数上下文环境==>对应的函数使用域
var a = 10
b = 20
function fn(x) {
    var a = 100,
        c = 300
    console.log("fn()", a, b, c, x) // fn() 100 20 300 10
    function bar(x) {
        var a = 1000,
            d = 400
        console.log("bar()", a, b, c, d, x) // bar() 1000 20 300 400 10
    }

    bar(100)
    bar(200)
}
fn(10)

作用域.jpg

5. 内存溢出与内存泄露

  1. 内存溢出
    • 一种程序运行出现的错误
    • 当程序运行需要的内存超过剩下的内存时, 就会抛出内存溢出的错误
  2. 内存泄露
    • 占用的内存没有及时释放
    • 内存泄露积累多了就容易导致内存溢出
    • 常见的内存泄露:
      • 意外的全局变量
      • 没有及时清理计时器或回调函数
      • 闭包

第三章 对象高级

1. 对象创建模式

1.1 Object 构造函数模式
  • 套路: 先创建空 Object 对象, 再动态添加属性/方法
  • 使用场景: 起始时不确定对象的内部数据
  • 问题: 语句太多
var p = new Object()
p.name = "Tom"
p.age = 12
p.setName = name => (this.name = name)

// 测试
p.setName("Jack")
console.log(p.name, p.age)
1.2 对象字面量模式
  • 套路: 使用{}创建对象, 同时指定属性/方法
  • 使用场景: 起始时对象内部数据是确定的
  • 问题: 如果创建多个对象, 有重复代码
var p = {
    name: "Tom",
    age: 12,
    setName(name) {
        this.name = name
    },
}
// 测试
console.log(p.name, p.age)
1.3 工厂模式
  • 套路: 通过工厂函数动态创建对象并返回
  • 使用场景: 需要多个对象
  • 问题: 对象没有一个具体的类型, 都是 Object 类型
function createPerson(name, age) {
    // 返回一个对象的函数===>工厂函数
    var obj = {
        name: name,
        age: age,
        setName(name) {
            this.name = name
        },
    }
    return obj
}

// 测试
var p1 = createPerson("Tom", 12)
var p2 = createPerson("Bob", 12)
1.4 自定义构造函数模式
  • 套路: 自定义构造函数,通过 new 创建对象
  • 使用场景: 需要创建多个类型确定的对象
  • 问题: 每个对象都有相同的数据, 浪费内存
function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = name => (this.name = name)
}

var p1 = new Person("Tom", 12)
1.5 构造函数+原型的组合模式
  • 套路: 自定义构造函数,属性在函数中初始化,方法添加到原型上
  • 使用场景: 需要创建多个类型确定的对象
function Person(name, age) {
    // 在构造函数只初始化一般函数
    this.name = name
    this.age = age
}
Person.prototype.setName = name => (this.name = name)

2. 继承模式

2.1 原型链继承
  1. 套路
    • 定义父类型构造函数
    • 给父类型的原型添加构造函数
    • 定义子类型的构造函数
    • 创建父类型的对象赋值给子类型的原型
    • 将子类型原型的构造属性设置为子类型
    • 给子类型原型添加对象
    • 创建子类型的对象: 可以调用父类型的方法
  2. 关键
    • 子类型的原型为父类型的一个实例对象
// 父类型
function Supper() {
    this.supProp = "Supper property"
}
Supper.prototype.showSupperProp = () => console.log(this.supProp)

// 子类型
function Sub() {
    this.subProp = "Sub property"
}
// 子类型的原型为父类的一个实例对象
Sub.prototype = new Supper()
// 让子类型的原型的constructor指向子类型
Sub.prototype.constructor = Sub
Sub.prototype.showSubProp = () => console.log(this.subProp)

var sub = new Sub()
sub.showSubProp()
sub.showSupperProp()
console.log(sub.constructor) // Sub
2.2 组合继承(原型链+构造函数)
  1. 利用原型链实现对父类型对象的方法继承
  2. 利用 supper() 借用父类型构建函数初始化相同属性
function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.setName = name => (this.name = name)

function Student(name, age, price) {
    // 为了得到属性
    Person.call(this, name, age)
    this.price = price
}
//为了能看到父类型的方法
Student.prototype = new Person()
// 修正constructor属性
Student.prototype.constructor = Student
Student.prototype.setPrice = price => (this.price = price)

var s = new Student("Tom", 12, 10000)
s.setName("Bob")
s.setPrice(15000)
console.log(s.name, s.age, s.price)

第四章 线程机制和事件

1. 浏览器的时间循环(轮询)

  1. 多有代码分类
    • 初始化执行代码(同步代码): 包含 dom 事件监听, 设置定时器, 发送 ajax 请求的代码
    • 回调执行代码(异步代码): 处理回调逻辑
  2. js 引擎执行代码的基本流程
    • 初始化代码===>回调代码
  3. 模型的 2 个重要组成部分
    • 事件管理模块
    • 回调队伍
  4. 模型的运转流程
    • 执行初始化代码, 将事件回调函数交给对应模块管理
    • 当事件发生时, 管理模块会将回调函数及其数据添加到回调队列中
    • 只有当初始化代码执行完后(可以要一定时间), 才会遍历读取回调队列中的回调函数执行
      浏览器的事件循环(轮询)模型.jpg

2. Web Workers 测试

  1. H5 规范了 js 分线程的实现, 取名为: Web Workers
  2. 相关 API
    • Worker: 构造函数, 加载分线程执行的 js 文件
    • Worker.prototype.onmessage: 用于接收一个线程的回调函数
    • Worker.prototype.postMessage: 向另一个线程发送消息
  3. 不足
    • worker 内代码不能操作 DOM(更新 UI)
    • 不能跨越加载 JS
    • 不是每个浏览器都支持这个新特性
<!-- html -->
<input type="text" placeholder="数值" id="number" />
<button id="btn">计算</button>
<script>
    var btn = document.getElementById("btn")
    var input = document.getElementById("number")
    btn.onclick = function () {
        var number = input.value

        // 创建一个Worker对象
        var worker = new Worker("worker.js")
        // 绑定接收消息的监听
        worker.onmessage = function (event) {
            console.log("主线程接收分线程返回的数据:" + event.data)
            alert(event.data)
        }

        // 向分线程发送消息
        worker.postMessage(number)
        console.log("主线程向分线程发送数据:" + number)
    }
</script>

<!-- worker.js -->
<script>
    /** 递归调用
     * f(n)=f(n-1)+f(n-2)
     * 1 1 2 3 5 8
     * @param {*} n
     * @return {*}
     */
    function fibonacci(n) {
        return n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2)
    }

    var onmessage = function (event) {
        var number = event.data
        console.log("分线程接收主线程返回的数据:" + number)
        // 计算
        var result = fibonacci(number)
        postMessage(result)
        console.log("分线程向主线程返回数据:" + result)
    }
</script>

线程图解.jpg

  • 多线程
    多线程(图解).jpg
已标记关键词 清除标记
相关推荐
<div class="lemma-summary"> <div class="para">JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的高级<a href="https://baike.baidu.com/item/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/9845131" target="_blank" rel="noopener" data-lemmaid="9845131">编程语言</a>。虽然它是作为开发<a href="https://baike.baidu.com/item/Web/150564" target="_blank" rel="noopener" data-lemmaid="150564">Web</a>页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如<a href="https://baike.baidu.com/item/%E5%87%BD%E6%95%B0/301912" target="_blank" rel="noopener" data-lemmaid="301912">函数</a>式编程)风格。<sup class="sup--normal" data-sup="1" data-ctrmap=":1,"> [1]</sup><a class="sup-anchor" name="ref_[1]_16168"></a> </div> <div class="para">JavaScript在1995年由<a href="https://baike.baidu.com/item/Netscape/2778944" target="_blank" rel="noopener" data-lemmaid="2778944">Netscape</a>公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。因为Netscape与<a href="https://baike.baidu.com/item/Sun/69463" target="_blank" rel="noopener" data-lemmaid="69463">Sun</a>合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。但实际上它的语法风格与<a href="https://baike.baidu.com/item/Self/4959923" target="_blank" rel="noopener" data-lemmaid="4959923">Self</a>及<a href="https://baike.baidu.com/item/Scheme/8379129" target="_blank" rel="noopener" data-lemmaid="8379129">Scheme</a>较为接近。<sup class="sup--normal" data-sup="2" data-ctrmap=":2,"> [2]</sup><a class="sup-anchor" name="ref_[2]_16168"></a> </div> <div class="para">JavaScript的标准是<a href="https://baike.baidu.com/item/ECMAScript%20/1889420" target="_blank" rel="noopener" data-lemmaid="1889420">ECMAScript </a>。截至 2012 年,所有浏览器都完整的支持ECMAScript 5.1,旧版本的浏览器至少支持ECMAScript 3 标准。2015年6月17日,ECMA国际组织发布了ECMAScript的第六版,该版本正式名称为 ECMAScript 2015,但通常被称为ECMAScript 6 或者ES6。<sup class="sup--normal" data-sup="1" data-ctrmap=":1,"> [1]</sup><a class="sup-anchor" name="ref_[1]_16168"></a> </div> </div>
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页