IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> javaScript进阶 -> 正文阅读

[JavaScript知识库]javaScript进阶

一、基础语法和数据类型

1、数据类型的分类和判断

  • 基本(值)类型

    • Number ----- 任意数值 -------- typeof
    • String ----- 任意字符串 ------ typeof
    • Boolean ---- true/false ----- typeof
    • undefined — undefined ----- typeof/=== 代表定义未赋值
    • null -------- null ---------- === 定义了且赋值为null
    1. undefined和null的区别?——undefined代表定义未赋值;null代表 定义了且赋值为null
    2. 什么时候赋值为null?
      • var a = null //a将指向一个对象, 但对象此时还没有确定(初始赋值为null,表明将要赋值成为对象)
      • a = null //让a指向的对象成为垃圾对象(被垃圾回收器回收)
    3. 严格区别变量类型与数据类型?
      • js的变量本身是没有类型的, 变量的类型实际上是变量内存中数据的类型
      • 变量类型:
        • 基本类型: 保存基本类型数据的变量
        • 引用类型: 保存对象地址值的变量
      • 数据类型
        • 基本类型
        • 对象类型
  • 对象(引用)类型

    • Object ----- typeof/ instanceof
    • Array ------ instanceof
    • Function ---- typeof
  1. typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型
    它返回值是一个字符串,该字符串说明运算数的类型。

  2. 对于 Array,null ,Object等特殊对象使用 typeof 一律返回 object,不能得到真实的数据类型。这正是 typeof 的局限性,引入instanceof。

  3. instanceof用来判断对象,代码形式为obj1 instanceof obj2(obj1是否是obj2的实例),obj2必须为对象,否则会报错!其返回值为布尔值。

  4. instanceof可以对不同的对象实例进行判断,判断方法是根据对象的原型链依次向下查询,如果obj2的显式原型对象存在obj1的原型链上,(obj1 instanceof obj2)值为true。

  5. 只要是输出console.log,都会出现undefined,因为undefined是它的返回值。

    console.log(console.log("www"));
    

    结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6KzGgC2-1627826771684)(C:\Users\mi\AppData\Roaming\Typora\typora-user-images\image-20210726223250643.png)]

  6. Typeof()返回null的类型为:Object?

    1995年JavaScript语言的第一版,所有值都设计成32位,
    其中最低的3位用来表述数据类型,object对应的值是000。
    当时,只设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),
    完全没考虑null,只把它当作object的一种特殊值,32位全部为0。
    这是typeof null返回object的根本原因。

2、数据,变量, 内存的理解

1、概念

  • 什么是数据?

    • 在内存中可读的, 可传递的保存了特定信息的’东东’,本质上是0101…
    • 可传递、可运算
    • 一切皆数据, 函数也是数据
    • 在内存中的所有操作的目标: 数据
      1. 算术运算
      2. 逻辑运算
      3. 赋值
      4. 调用函数传参
  • 什么是变量?

    • 在程序运行过程中它的值是允许改变的量,由变量名和变量值组成
    • 一个变量对应一块小内存, 变量名用来查找到内存, 变量值就是内存中保存的内容
  • 什么是内存?

    • 内存条通电后产生的存储空间(临时的)
    • 产生和死亡: 内存条(集成电路板) == >通电==>产生一定容量的存储空间==>存储各种数据==>断电==>内存s数据和空间全部消失
    • 内存的空间是临时的, 而硬盘的空间是持久的
    • 一块内存包含2个方面的数据
      • 内部存储的数据
      • 地址值数据
    • 内存空间的分类
      • 栈空间: 全局变量和局部变量
      • 堆空间: 对象
  • 内存,数据, 变量三者之间的关系

    • 内存是容器, 用来存储不同数据
    • 变量是内存的标识, 通过变量我们可以操作(读/写)内存中的数据

2、关于引用变量赋值问题

 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象

3、问题: 在js调用函数时传递变量参数时, 是值传递还是引用传递

  1. 只有值传递, 没有引用传递, 传递的都是变量的值, 只是这个值可能是基本数据, 也可能是地址(引用)数据
  2. 如果后一种看成是引用传递, 那就值传递和引用传递都可以有
  3. 总结:函数传参数的时候,输入的是基本类型,则传的是值,值没有进行改变;输入的是对象类型,则传的是地址,改变了对象里的属性值。

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

  1. 内存生命周期

    • 分配需要的内存
    • 使用分配到的内存
    • 不需要时将其释放/归还
  2. 释放内存

    • 为执行函数分配的栈空间内存: 函数执行完自动释放
  • 存储对象的堆空间内存: 当内存没有引用指向时, 对象成为垃圾对象, 垃圾回收器后面就会回收释放此内存

3、对象的理解和使用

  • 什么是对象?

    • 多个数据(属性)的集合
    • 用来保存多个数据(属性)的容器
    • 一个对象代表现实中的一个事物
  • 为什么要用对象?

    • 统一管理多个数据
  • 对象的组成?

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

    • 属性名 : 字符串(标识)
    • 属性值 : 任意类型
  • 属性的分类:

    • 一般 : 属性值不是function 描述对象的状态
    • 方法 : 属性值为function的属性 描述对象的行为
  • 特别的对象–数组和函数

    • 数组: 属性名是0,1,2,3之类的索引
    • 函数: 可以执行的
  • 如何操作内部属性(方法)

    • .属性名

    • <script type="text/javascript">
        // 创建对象
        var p = {}
      
      /*情形一: 属性名不是合法的标识名*/
        /*需求: 添加一个属性: content-type: text/json */
        //  p.content-type = 'text/json' //不正确
        p['content-type'] = 'text/json'
      
      /*情形二: 属性名不确定*/
        var prop = 'xxx'
        var value = 123
        // p.prop = value  //不正确
        p[prop] = value
        console.log(p['content-type'], p[prop])
      </script>
      

4、函数的理解和使用

  • 什么是函数?

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

    • 提高复用性
    • 便于阅读交流
  • 如何定义函数

    • 函数声明

      function fn1(){
      	console.log("fn1")
      }
      
    • 表达式声明

      var fn2=function(){
      	console.log("fn2")
      }
      
  • 如何调用执行函数

    • test()直接调用
    • obj.test() 通过对象调用
    • new test() new调用
    • test.apply/call(obj) :相当于obj.test() 临时让test成为obj的方法并调用 可以让一个函数成为指定任意对象的方法进行调用
  • 函数也是对象

    • instanceof Object===true
    • 函数有属性: prototype 显示原型
    • 函数有方法: call()/apply()
    • 可以添加新的属性/方法
  • 函数的3种不同角色

    • 一般函数 : 直接调用
    • 构造函数 : 通过new调用
    • 对象 : 通过调用内部的属性/方法
  • 函数中的this

    • 显式指定谁: obj.xxx()
    • 通过call/apply指定谁调用: xxx.call(obj)
    • 不指定谁调用: xxx() : window
    • 回调函数: 看背后是通过谁来调用的: window/其它
  • 匿名函数自调用:

    (function(w, obj){
      //实现代码
    })(window, obj)
    
    • 专业术语为: IIFE (Immediately Invoked Function Expression) 立即调用函数表达式

    • 作用

      * 隐藏内部实现

      * 不污染外部(全局)命名空间

  • 回调函数的理解

    • 什么函数才是回调函数?

      • 你定义的
      • 你没有调用
      • 但它最终执行了(在一定条件下或某个时刻)
    • 常用的回调函数

      • dom事件回调函数

      • 定时器回调函数

      • ajax请求回调函数(后面讲解)

      • 生命周期回调函数(后面讲解)

5、函数中的this

1、this是什么?

  1. 一个关键字, 一个内置的引用变量
  2. 在函数中都可以直接使用this
  3. this代表调用函数的当前对象
  4. 在定义函数时, this还没有确定, 只有在执行时才**动态确定(**绑定)的

2、如何确定this的值?

* test()

* obj.test()

* new test()

* test.call(obj)

前置知识:

本质上:任何函数在执行时都是通过某个对象调用的

二、函数高级

1、原型与原型链

  • 所有函数都有一个特别的属性:

    • prototype : 显式原型属性
  • 所有实例对象都有一个特别的属性:

    • __proto__ : 隐式原型属性
  • 显式原型与隐式原型的关系

    • 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
    • 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值(实例对象的_proto_指向构造函数的prototype(即是原型对象))
    • 原型对象即为当前实例对象的父对象
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MJaS6oq-1627826771688)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSFbNMu8-1627826771693)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yIZruTgu-1627826771698)(E:\小组阶段学习\javascript\尚硅谷JavaScript\尚硅谷JavaScript高级教程\源码_课件\JSAdvance\work\02_函数高级\01_原型与原型链\显式原型与隐式原型.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ogHEAT2-1627826771705)(E:\小组阶段学习\javascript\尚硅谷JavaScript\尚硅谷JavaScript高级教程\源码_课件\JSAdvance\work\02_函数高级\01_原型与原型链\原型链分析.png)] 总结:
      1. 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象(但是Object的prototype的__proto__是null)
      2. 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
      3. 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
  • 原型链

    • 所有的实例对象都有__proto__属性, 它指向的就是原型对象

    • 这样通过__proto__属性就形成了一个链的结构---->原型链

    • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找

    • 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作

    • <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>03_原型链</title>
      </head>
      <body>
      <!--
      1. 原型链(图解)
        * 访问一个对象的属性时,
          * 先在自身属性中查找,找到返回
          * 如果没有, 再沿着__proto__这条链向上查找, 找到返回
          * 如果最终没找到, 返回undefined
        * 别名: 隐式原型链
        * 作用: 查找对象的属性(方法)
      2. 构造函数/原型/实体对象的关系(图解)
      3. 构造函数/原型/实体对象的关系2(图解)
      -->
      <script type="text/javascript">
        function Fn() {
          this.test1 = function () {
            console.log('test1()')
          }
        }
        Fn.prototype.test2 = function () {
          console.log('test2()')
        }
        var fn = new Fn()
      
        fn.test1()
        fn.test2()
        console.log(fn.toString())
        // fn.test3()
      
      
        // 1、函数的显式原型指向的对象是默认的空Object实例对象(但Object的显示原型的隐式原型指向null)
        console.log(Fn.prototype instanceof Object)//true
        console.log(Object.prototype instanceof Object);//fasle
        console.log(Function.prototype instanceof Object);//true
        //2、所有的函数都是Function的实例(包含Function)
        console.log(Fn instanceof Function);//true
        console.log(Function instanceof Function);//true
        console.log(Function.__proto__==Function.prototype);//true
        // 3、Object的原型对象是原型链的尽头
        console.log(Object.prototype.__proto__);//null
      </script>
      </body>
      </html>
      
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>04_原型链_属性问题</title>
</head>
<body>
<!--
1. 读取对象的属性值时: 会自动到原型链中查找
2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
-->
<script type="text/javascript">
  function fn(){

  }
  fn.prototype.name="嘻嘻嘻"

  var a=new fn();
  console.log(a.name,a)

  var b=new fn()
  b.name="哈哈哈"
  console.log(a.name,b.name,b)


  function Person(name, age) {
    this.name = name;
    this.age = age;
  }
  Person.prototype.setName = function (name) {
    this.name = name;
  }
  Person.prototype.sex = '男';

  var p1 = new Person('Tom', 12)
  p1.setName('Jack')
  console.log(p1.name, p1.age, p1.sex)
  p1.sex = '女'
  console.log(p1.name, p1.age, p1.sex)

  var p2 = new Person('Bob', 23)
  console.log(p2.name, p2.age, p2.sex)

</script>
</body>
</html>

在这里插入图片描述
原型链经典面试题:


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>06_面试题</title>
</head>
<body>

<script type="text/javascript">
/*
测试题1
*/
var A = function() {

}
A.prototype.n = 1

var b = new A()

A.prototype = {
 n: 2,
 m: 3
}

var c = new A()
console.log(b.n, b.m, c.n, c.m)//1,undefined,2,3


/*
测试题2
*/
var F = function(){};
Object.prototype.a = function(){
 console.log('a()')
};
Function.prototype.b = function(){
 console.log('b()')
};
var f = new F();
f.a()//a()
f.b()//报错
F.a()//a()
F.b()//b()

</script>
</body>
</html>

2、执行上下文与执行上下文栈

  • 变量提升与函数提升

    • 变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined)
    • 函数提升: 在函数定义语句之前, 就执行该函数
    • 先有变量提升, 再有函数提升
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>01_变量提升与函数提升</title>
</head>
<body>
<!--
1. 变量声明提升
  * 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  * 值: undefined
2. 函数声明提升
  * 通过function声明的函数, 在之前就可以直接调用
  * 值: 函数定义(对象)
3. 问题: 变量提升和函数提升是如何产生的?
-->
<script type="text/javascript">

  /*
   面试题: 输出什么?  答案:undefined
   */
  var a = 4
  function fn () {
     //1、
    console.log(a)
    var a = 5
    //上面的相当于下面的
    //2、
    var a
    console.log(a)
    a=5
  }
  fn()//undefined


  /*变量提升*/
  console.log(a1) //可以访问, 但值是undefined
  /*函数提升*/
  a2() // 可以直接调用 a2()

  var a1 = 3
  function a2() {
    console.log('a2()')
  }
</script>
</body>
</html>
  • 理解

    • 执行上下文: 由 js 引擎自动创建的对象, 包含对应作用域中的所有变量属性
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>02_执行上下文</title>
</head>
<body>
<!--
1. 代码分类(位置)
 * 全局代码
 * 函数代码
2. 全局执行上下文
 * 在执行全局代码前将window确定为全局执行上下文
 * 对全局数据进行预处理
   * var定义的全局变量==>undefined, 添加为window的属性
   * function声明的全局函数==>赋值(fun), 添加为window的方法
   * this==>赋值(window)
 * 开始执行全局代码
3. 函数执行上下文
 * 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的,存在于栈中)
 * 对局部数据进行预处理
   * 形参变量==>赋值(实参)==>添加为执行上下文的属性
   * arguments==>赋值(实参列表), 添加为执行上下文的属性
   * var定义的局部变量==>undefined, 添加为执行上下文的属性
   * function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
   * this==>赋值(调用函数的对象)
 * 开始执行函数体代码
-->
<script type="text/javascript">
//全局执行上下文
 console.log(a1,window.a1)//undefined;undefined
 console.log(a2,window.a2)//报错,a2未定义;undefined
 console.log(a3,window.a3)//undefined;undefined
 console.log(a4,window.a4)//? a4() {console.log('a4()')}
 console.log(this)//Window

 var a1 = 3
 a2= 4
 var a3 = function () {
   console.log('a3()')
 }
 function a4() {
   console.log('a4()')
 }


//函数执行上下文
 function fn(x, y) {
   console.log(x, y)//undefined,undefined
   console.log(b1)//undefined
   console.log(b2)//? b2 () {}
   console.log(arguments)//Arguments伪数组
   console.log(this)// Window

   // console.log(b3)//出错

   var b1 = 5
   function b2 () {

   }
   b3 = 6
 }
 fn()

</script>
</body>
</html>
  • 执行上下文栈: 用来管理产生的多个执行上下文
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03_执行上下文栈</title>
</head>
<body>
<!--
1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
4. 在当前函数执行完后,将栈顶的对象移除(出栈)
5. 当所有的代码执行完后, 栈中只剩下window
-->
<script type="text/javascript">
//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) //2. 进入bar函数执行上下文
</script>

</body>
</html>
  • 运行结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jtm2jCrY-1627827934473)(C:\Users\mi\AppData\Roaming\Typora\typora-user-images\image-20210729143828208.png)]

  • 面试题:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_面试题</title>
</head>
<body>
<script type="text/javascript">
/*
测试题1: 先预处理变量, 后预处理函数
*/
function a() {}
var a;
console.log(typeof a)//'function'


/*
测试题2: 变量预处理, in操作符
*/
if (!(b in window)) {
 var b = 1;
}
console.log(b)//undefined

/*
测试题3: 预处理, 顺序执行
*/
var c = 1
function c(c) {
 console.log(c)
 var c = 3
}
c(2)//报错 c is not a function

</script>
</body>
</html>
  • 分类:

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

    • 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡
    • 函数 : 调用函数时产生, 函数执行完时死亡
  • 包含哪些属性:

    • 全局 :
      • 用var定义的全局变量 ==>undefined
      • 使用function声明的函数 ===>function
      • this ===>window
    • 函数
      • 用var定义的局部变量 ==>undefined
      • 使用function声明的函数 ===>function
      • this ===> 调用函数的对象, 如果没有指定就是window
      • 形参变量 ===>对应实参值
      • arguments ===>实参列表的伪数组
  • 执行上下文创建和初始化的过程

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

3、作用域与作用域链

  • 理解:

    • 作用域: 一块代码区域, 在编码时就确定了, 不会再变化
    • 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量
  • 分类:

    • 全局作用域
    • 函数作用域
    • js没有块作用域(在ES6之前)
  • 作用

    • 作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突
    • 作用域链: 查找变量
      • 理解
        • 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
        • 查找变量时就是沿着作用域链来查找的
      • 查找一个变量的查找规则
        • 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
        • 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
        • 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
  • 区别作用域与执行上下文

    • 区别1
      • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
      • 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
      • 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建
    • 区别2
      • 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
      • 执行上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会自动释放
    • 联系
      • 执行上下文环境(对象)是从属于所在的作用域
      • 全局上下文环境==>全局作用域
      • 函数上下文环境==>对应的函数使用域
  • 面试题:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>04_作用域_面试题</title>
</head>
<body>
<script type="text/javascript">
  /*
   问题: 结果输出多少?
   */
  var x = 10;
  function fn() {
    console.log(x);//10
  }
  function show(f) {
    var x = 20;
    f();
  }
  show(fn);


  var y= 10;
  function show2() {
    var y = 20;
    console.log(y);//20
  }
  show2()
    
  
    
 /*
   说说它们的输出情况
   */

  var fn = function () {
    console.log(fn)
  }
  fn()
  // ? () {
  //   console.log(fn)
  // }

  var obj = {
    fn2: function () {
      // console.log(fn2)//出错,出现fn2没定义
      console.log(this.fn2);//成功? () {/ console.log(fn2)//出错,出现fn2没定义console.log(this.fn2);}
    }
  }
  obj.fn2()
</script>
</body>
</html>

4、闭包

  • 理解:

    • 当嵌套的内部函数引用了外部函数的变量时就产生了闭包
    • 通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性
  • 写一个闭包程序

<!--
1. 如何产生闭包?
  * 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
2. 闭包到底是什么?
  * 使用chrome调试查看
  * 理解一: 闭包是嵌套的内部函数(绝大部分人)
  * 理解二: 包含被引用变量(函数)的对象(极少数人)
  * 注意: 闭包存在于嵌套的内部函数中
3. 产生闭包的条件?
  * 函数嵌套
  * 内部函数引用了外部函数的数据(变量/函数)
判断闭包的个数:外部函数的调用次数或者内部函数的建立次数
-->
<script>
function fn1() {
  var a = 2;
  function fn2() {
    a++;
    console.log(a);
  }
  return fn2;
}
var f = fn1();
f();
f();
</script>
  • 闭包的应用形式:

    • 将函数作为另一个函数的返回值
<script type="text/javascript">
// 将函数作为另一个函数的返回值
  function fn1() {
    //此处闭包已经产生(函数提升,内部函数对象已经创建)
    var a = 2

    function fn2() {
      a++    //引用外部函数的变量--->产生闭包
      console.log(a)
    }

    return fn2
  }
  var f = fn1()   //由于f引用着内部的函数-->内部函数以及闭包都没有成为垃圾对象
  f() // 3        //间接操作了函数内部的局部变量
  f() // 4
  f = null //此时闭包对象死亡(包含闭包的函数对象成为垃圾对象)
</script>
  • 将函数作为实参传递给另一个函数调用

    <script type="text/javascript">     
    // 将函数作为实参传递给另一个函数调用  
    function showMsgDelay(msg, time) {    
    setTimeout(function () {      
    	console.log(msg)//msg是引用的变量   
    }, time)  
    }  
    showMsgDelay('hello', 6000)    
    </script>
    
  • 闭包作用:

    • 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
    • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
    • 问题:
      • 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在的,存在于闭包中的变量才有可能存在
      • 在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包从外部操作它
  • 闭包的生命周期

    • 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
    • 死亡: 在嵌套的内部函数成为垃圾对象
  • 闭包应用:

    • 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为
    • 循环遍历加监听
    • JS框架(jQuery)大量使用了闭包
  • 缺点:

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

<script type="text/javascript">
  /*
   说说它们的输出情况
   */

  //代码片段一
  var name = "The Window";
  var object = {
    name: "My Object",
    getNameFunc: function () {
      return function () {
        return this.name;//this=window
      };
    }
  };
  console.log(object.getNameFunc()());  //?The Window

  //代码片段二
  var name2 = "The Window";
  var object2 = {
    name2: "My Object",
    getNameFunc: function () {
      var that = this;//this=object2
      return function () {
        return that.name2;
      };
    }
  };
  console.log(object2.getNameFunc()()); //?My Object
</script>
<script type="text/javascript">
  /*
   说说它们的输出情况
   */

  function fun(n, o) {
    console.log(o)
    return {
      fun: function (m) {
        return fun(m, n)
      }
    }
  }
  var a = fun(0)
  a.fun(1)  
  a.fun(3) //undefined,?,?,?

  var b = fun(0).fun(1).fun(2).fun(3) //undefined,?,?,?

  var c = fun(0).fun(1)
  c.fun(2)
  c.fun(3) //undefined,?,?,?

</script>

5、内存溢出与内存泄露

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

    • 占用的内存没有及时释放
    • 内存泄露积累多了就容易导致内存溢出
    • 常见的内存泄露:
      • 意外的全局变量
      • 没有及时清理的计时器或回调函数
      • 闭包
  2. 例子:

<script type="text/javascript">

  // 1. 内存溢出
  /*var obj = {}
  for (var i = 0; i < 100000; i++) {
    obj[i] = new Array(10000000)
  }
  console.log('------')*/

  // 2. 内存泄露
    // 意外的全局变量
  function fn () {
    a = [] //不小心没有var定义
  }
  fn()
    // 启动循环计时器不清理
 var setIntervalId= setInterval(function  () {
    console.log('----')
  }, 1000)
clearTnterval(setIntervalId);//清除定时器
</script>

三、对象高级

1、对象的创建模式

1、Object构造函数模式

  • 套路: 先创建空Object对象, 再动态添加属性/方法
  • 适用场景: 起始时不确定对象内部数据
  • 问题: 语句太多
<script>    
	var p = new Object()    
	p = {}    
	obj.name = 'Tom'    
	obj.setName = function(name){this.name=name}
</script>

2、对象字面量模式

  • 套路: 使用{}创建对象, 同时指定属性/方法

  • 适用场景: 起始时对象内部数据是确定的

  • 问题: 如果创建多个对象, 有重复代码

<script>
var p = {
    name: 'Tom',
    age: 23,
    setName: function (name) {
      this.name = name
    }
}
console.log(p.name, p.age)//Tom 23
p.setName('JACK')
console.log(p.name, p.age)//JACK 23
    
//如果创建多个对象代码重复
var p2 = {
    name: 'BOB',
    age: 24,
    setName: function (name) {
      this.name = name
    }
}
</script>

3、工厂模式

  • 套路: 通过工厂函数动态创建对象并返回
  • 适用场景: 需要创建多个对象
  • 问题: 对象没有一个具体的类型, 都是Object类型
<script>
// 工厂函数: 返回一个对象的函数
  function createPerson(name, age) {
    var p = {
      name: name,
      age: age,
      setName: function (name) {
        this.name = name
      }
    }
    return p
  }

  var p1 = createPerson('Tom', 12)
  var p2 = createPerson('JAck', 13)
  console.log(p1)
  console.log(p2)
</script>

在这里插入图片描述

4、构造函数模式

  • 套路: 自定义构造函数, 通过new创建对象

  • 适用场景: 需要创建多个类型确定的对象

  • 问题: 每个对象都有相同的数据, 浪费内存

<script type="text/javascript">
  function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = function (name) {
      this.name = name
    }
  }

  var p1 = new Person('Tom', 12)
  var p2 = new Person('Tom2', 13)
  console.log(p1, p1 instanceof Person)
  
  function Student(name,price){
    this.name=name;
    this.price=price;
  }
  var s=new Student("Bob",12000);
  console.log(s instanceof Student)
</script>

5、构造函数+原型的组合模式

  • 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上

  • 适用场景: 需要创建多个类型确定的对象

<script>
    function Person(name, age) {

         this.name = name;

         this.age = age;

    }

    Person.prototype.setName = function(name){this.name=name;};

    new Person('tom', 12);
</script>

2、继承模式

1、原型链继承 : 得到方法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>01_原型链继承</title>
</head>
<body>
<!--
方式1: 原型链继承
  1. 套路
    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法
  2. 关键
    1. 子类型的原型为父类型的一个实例对象
-->
<script type="text/javascript">

  function Supper() { //父类型
    this.superProp = 'The super prop'
  }
  //原型的数据所有的实例对象都可见
  Supper.prototype.showSupperProp = function () {
    console.log(this.superProp)
  }

  function Sub() { //子类型
    this.subProp = 'The sub prop'
  }

  // 子类的原型为父类的实例
  Sub.prototype = new Supper()
  // 修正Sub.prototype.constructor为Sub本身
  Sub.prototype.constructor = Sub

  Sub.prototype.showSubProp = function () {
    console.log(this.subProp)
  }

  // 创建子类型的实例
  var sub = new Sub()
  // 调用父类型的方法
  sub.showSubProp()//The sub prop
  // 调用子类型的方法
  sub.showSupperProp()//The super prop
</script>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-305vhtZF-1627826771714)(F:\图片\小组\未命名文件.png)]

2、 借用构造函数 : 得到属性

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>02_借用构造函数继承</title>
</head>
<body>
<!--
方式2: 借用构造函数继承(假的)
1. 套路:
  1. 定义父类型构造函数
  2. 定义子类型构造函数
  3. 在子类型构造函数中调用父类型构造
2. 关键:
  1. 在子类型构造函数中通用call()调用父类型构造函数
-->
<script type="text/javascript">

  function Person(name, age) {
    this.name = name
    this.age = age
  }

  function Student(name, age, price) {
    Person.call(this, name, age)   // this.Person(name, age)
    this.price = price
  }

  var s = new Student('Tom', 20, 12000)
  console.log(s.name, s.age, s.price)//'Tom', 20, 12000
</script>
</body>
</html>

3、组合

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>03_组合继承</title>
</head>
<body>
<!--
方式3: 原型链+借用构造函数的组合继承
1. 利用原型链实现对父类型对象的方法继承
2. 利用call()借用父类型构建函数初始化相同属性
-->
<script type="text/javascript">

  function Person(name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }

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

  var s = new Student('Tom', 12, 10000)
  s.setPrice(11000)
  s.setName('Bob')
  console.log(s)
  console.log(s.constructor)

</script>
</body>
</html>

4、new一个对象背后做了些什么?

创建一个空对象给对象设置__proto__, 
值为构造函数对象的prototype属性值  this.__proto__= Fn.prototype执行构造函数体
(给对象添加属性/方法)

四、线程机制和事务机制

1、线程与进程

  1. 进程:

    • 程序的一次执行, 它占有一片独有的内存空间
    • 可以通过windows任务管理器查看进程
  2. 线程:

    • 是进程内的一个独立执行单元
    • 是程序执行的一个完整流程
    • 是CPU的最小的调度单元
  3. 关系

    • 一个进程中一般至少有一个运行的线程: 主线程(进程启动后自动创建)
    • 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行
    • 一个进程内的数据可以供其中的多个线程直接共享
    • 多个进程之间的数据是不能直接共享的
  4. 相关知识:

    • 应用程序必须运行在某个进程的某个线程上
    • 线程池(thread pool):保存多个线程对象的容器,实现线程对象的反复利用
  5. 何为多进程和多线程?

    • 多进程:一个应用程序可以同时启动多个实例运行
    • 多线程:在一个进程里,有多个线程运行
  6. 比较多线程和单线程

    • 多线程:
      • 优点:能有效提高CPU的利用率
      • 缺点:创建多线程开销;线程间切换开销;死锁与状态同步问题
    • 单线程:
      • 优点:顺序编程简单易懂
      • 缺点:效率低
  7. JS是单线程的还是多线程的?

    • js是单线程运行的
    • 但使用了H5中的Web workers可以多线程运行
  8. 浏览器运行是单进程还是多进程?

    有的是单进程

     firefox
     
     老版IE
    

    有的是多进程

     chrome
     
     新版IE
    
  9. 如何查看浏览器是否是多进程运行的呢?

    任务管理器==>进程

  10. 浏览器运行是单线程还是多线程?

    都是多线程运行的
    

2、浏览器内核模块组成

  1. 什么是浏览器内核?

    支持浏览器运行的最核心的程序

  2. 不同的浏览器可能不太一样

    * Chrome, Safari: webkit

    * firefox: Gecko

    * IE: Trident

    * 360,搜狗等国内浏览器: Trident + webkit

  3. 主线程

    • js引擎模块 : 负责js程序的编译与运行
    • html,css文档解析模块 : 负责页面文本的解析
    • DOM/CSS模块 : 负责dom/css在内存中的相关处理
    • 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)
  4. 分线程

    • 定时器模块 : 负责定时器的管理
    • DOM事件响应模块 : 负责事件的管理
    • 网络请求模块 : 负责Ajax请求

3、js线程

  1. 如何证明js执行是单线程的?

    * setTimeout()的回调函数是在主线程执行的

    * 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行

  2. 为什么js要用单线程模式, 而不用多线程模式?

    * JavaScript的单线程,与它的用途有关。

    * 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。

    * 这决定了它只能是单线程,否则会带来很复杂的同步问题

  3. 代码的分类:

    初始化代码

    回调代码

  4. js引擎执行代码的基本流程

    * 先执行初始化代码: 包含一些特别的代码

    ? 设置定时器

      	绑定监听
     
     	发送ajax请求
    

    *后面在某个时刻才会执行回调代码

  5. H5提出了实现多线程的方案: Web Workers

  6. 只能是主线程更新界面

  7. <script type="text/javascript">
      setTimeout(function () {
        console.log('timeout 3')
      }, 3000)
    
      setTimeout(function () {
        console.log('timeout 2')
        alert('2222')
      }, 2000)
    
      alert('提示...')
      console.log('alert之后')
    </script>
    

4、定时器问题:

  1. 问:定时器真的是定时执行嘛?

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

  2. 问:定时器回调函数是在分线程执行的嘛?

    答:在主线程执行的,js是单线程的

  3. 问:定时器是如何实现的?

    答:事件循环模型

  4. <script type="text/javascript">
      document.getElementById('btn').onclick = function () {
        var start = Date.now()
        console.log('启动定时器')
        setTimeout(function () {
          console.log('定时器执行了: ', Date.now()-start)
        }, 100)
    
        //定时器启动之后做一个长时间的工作
        for (var i = 0; i < 1000000000; i++) {
    
        }
        console.log('完成长时间工作', Date.now()-start)
      }
    </script>
    

5、事件处理机制(事件循环模型)

  1. 代码分类

    初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码

    回调执行代码: 处理回调逻辑

  2. js引擎执行代码的基本流程: 初始化代码===>回调代码

  3. 模型的2个重要组成部分:

    • 事件(定时器、DOM事件、ajax)管理模块
    • 回调队列汇总要概念相关
  4. 相关重要概念:

    • 执行栈:所有的代码都是在此空间中执行的
    • 浏览器内核:
      • browser core
      • js引擎模块(在主线程处理)
      • 其他模块(在主\分线程处理)
    • 任务队列
    • 消息队列
    • 事件队列
    • 事件轮询:从任务队列中循环取出回调函数放入到执行栈中处理(一个接一个)
    • 事件驱动模型
    • 请求响应模型
  5. 模型的运转流程

    • 执行初始化代码, 将事件回调函数交给对应模块管理
    • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
    • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
  6. <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>05_事件循环模型</title>
    </head>
    <body>
    <button id="btn">测试</button>
    <script type="text/javascript">
      function fn1() {
        console.log('fn1()')
      }
      fn1()
    
      document.getElementById('btn').onclick = function () {
        console.log('处理点击事件')
      }
    
      setTimeout(function () {
        console.log('到点了')
      }, 2000)
    
      function fn2() {
        console.log('fn2()')
      }
      fn2()
    
    </script>
    </body>
    </html>
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MmFZhIX-1627826771716)(E:\小组阶段学习\javascript\尚硅谷JavaScript\尚硅谷JavaScript高级教程\源码_课件\JSAdvance\prepare\04_线程机制与事件机制\事件循环模型.png)]

6、H5 Web Workers

  1. H5规范提供了js分线程的实现, 取名为: Web Workers

  2. 相关API

    * Worker: 构造函数, 加载分线程执行的js文件

    * Worker.prototype.onmessage: 用于接收另一个线程的回调函数

    * Worker.prototype.postMessage: 向另一个线程发送消息

  3. 不足

    * worker内代码不能操作DOM(更新UI)

    * 不能跨域加载JS

    * 不是每个浏览器都支持这个新特性

  4. 例子(斐波拉契数列)

    (1)主线程

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>07_Web Workers_测试</title>
</head>
<body>
<!--
1. H5规范提供了js分线程的实现, 取名为: Web Workers
2. 相关API
  * Worker: 构造函数, 加载分线程执行的js文件
  * Worker.prototype.onmessage: 用于接收另一个线程的回调函数
  * Worker.prototype.postMessage: 向另一个线程发送消息
3. 不足
  * worker内代码不能操作DOM(更新UI)
  * 不能跨域加载JS
  * 不是每个浏览器都支持这个新特性
-->
<input type="text" name="" id="number"/>
<button id="btn">获取值</button>
<script type="text/javascript">

var input=document.getElementById("number");
document.getElementById("btn").onclick=function(){
  var number=input.value;
}
//
//创建 Worker对象
var worker=new Worker("worker.js")
//绑定接收信息的监听
worker.onmessage=function(event){
  alert(event.data)
}
//向分线程发送消息
worker.postMessage(number)
</script>
</body>
</html>

(2)worker.js文件(分线程)

function fi(n){
  return n<=2? 1:fi(n-1)+fi(n-2);
}
console.log(this)//
var onmessage=function(event){
  var number=event.data
  console.log("分线程接收到主线程发送的数据:"+number)
  var result=fi(number)
  postMessage(result)
  console.log("分线程传成主线程的数据:"+result)
  // alert("sss")//alert是window的方法,在分线程不能调用
  //分线程的全局对象不再是window,所以在分线程中不可能更新界面
}

3.问题:

  • worker内代码不能操作DOM更新UI
  • 不是每个浏览器都支持这个新特
  • 不能跨域加载JS
  • svn版本控制
  • svn server

4.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THLZOzS9-1627826771719)(E:\小组阶段学习\javascript\尚硅谷JavaScript\尚硅谷JavaScript高级教程\源码_课件\JSAdvance\prepare\04_线程机制与事件机制\H5 Web Workers(多线程)].png)

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-02 10:43:22  更:2021-08-02 10:44:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/22 1:16:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码