————————-基础总结深入
01.数据类型
01.数据类型的分类和判断
基本(值)类型
- Number ——- 任意数值 ———— typeof
- String ——- 任意字符串 ——— typeof
- Boolean —— true/false ——- typeof
- undefined —- undefined ——- typeof/===
- null ———— null ————— ===
对象(引用)类型
- Object ——-任意对象 typeof/instanceof
- Array ——— 一种特别的对象(数值下标操作,内部数据是有序的)instanceof
- Function —— 一种特别的对象(可以执行) typeof
判断类型是否为undefine
// 1.基本l l typeof返回数据类型的字符串表达var aconsole.log(a, typeof a, typeof a==='undefined'T // undefined 'undefined' tconsole.log(undefined==='undefined')//false
详细讲解过程
- ```html <!DOCTYPE html> <!—
- 分类
- 基本(值)类型
- String: 任意字符串
- Number: 任意的数字
- boolean: true/false
- undefined: undefined
- null: null
- 对象(引用)类型
- Object: 任意对象
- Function: 一种特别的对象(可以执行)
- Array: 一种特别的对象(数值下标, 内部数据是有序的)
- 基本(值)类型
- 判断
- typeof:
- 可以判断: undefined/ 数值 / 字符串 / 布尔值 / function
- 不能判断: null与object object与array
- instanceof:
- 判断对象的具体类型
- ===
- 可以判断: undefined, null —>
- typeof:
1). 你定义的
2). 你没有调
3). 但最终它执行了(在某个时刻或某个条件下) 2. 常见的回调函数? - dom事件回调函数 ==>发生事件的dom元素 - 定时器回调函数 ===>window - ajax请求回调函数(后面讲) - 生命周期回调函数(后面讲) ```html
<a name="01417456"></a>## 03.IIFE(立即执行函数表达式)1. 理解- 全称: Immediately-Invoked Function Expression2. 作用- 隐藏实现- 不会污染外部(全局)命名空间- 用它来编码js模块```html<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>06_IIFE</title></head><body><!--1. 理解* 全称: Immediately-Invoked Function Expression2. 作用* 隐藏实现* 不会污染外部(全局)命名空间* 用它来编码js模块--><script type="text/javascript">(function () { //匿名函数自调用var a = 3console.log(a + 3)})()var a = 4console.log(a);(function () {var a = 1function test () {console.log(++a)}window.$ = function () { // 向外暴露一个全局函数return {test: test}}})()$().test() // 1. $是一个函数 2. $执行后返回的是一个对象 很关键</script></body></html>
05.this上下文
- this是什么?
- 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
- 所有函数内部都有一个变量this
- 它的值是调用函数的当前对象
- 如何确定this的值?
- test(): window
- p.test(): p
- new test(): 新创建的对象
- p.call(obj): obj
- 1.以函数的形式调用时,this是windows
- 2.以方法的形式调用时,this就是调用方法的对象
- 3.以构造函数的形式调用时,this就是新创建的对象
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>07_函数中的this</title></head><body><!--1. this是什么?* 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window* 所有函数内部都有一个变量this* 它的值是调用函数的当前对象2. 如何确定this的值?* test(): window* p.test(): p* new test(): 新创建的对象* p.call(obj): obj--><script type="text/javascript">function Person(color) {console.log(this)this.color = color;this.getColor = function () {console.log(this)return this.color;};this.setColor = function (color) {console.log(this)this.color = color;};}Person("red"); //this是谁?//此时是作为函数使用 调用者没有明确指定//windowvar p = new Person("yello"); //this是谁?//p指向后面生成的对象//pp.getColor(); //this是谁?//p调用的函数//pvar obj = {};p.setColor.call(obj, "black"); //this是谁? objvar test = p.setColor;test(); //this是谁?//没有指定//windowfunction fun1() {function fun2() {console.log(this);}fun2(); //this是谁? window}fun1();</script></body></html>
———————-补充
01.分号问题
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_分号问题</title></head><body><!--1. js一条语句的后面可以不加分号2. 是否加分号是编码风格问题, 没有应该不应该,只有你自己喜欢不喜欢3. 在下面2种情况下不加分号会有问题* 小括号开头的前一条语句* 中方括号开头的前一条语句4. 解决办法: 在行首加分号5. 强有力的例子: vue.js库6. 知乎热议: https://www.zhihu.com/question/20298345--><script type="text/javascript">var a = 3;(function () {})()/*错误理解var a = 3(function () {})();*/var b = 4;[1, 3].forEach(function () {})/*错误理解var b = 4[3].forEach(function () {})*/</script></body></html>
02.内存溢出与内存泄漏
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_内存溢出与内存泄露</title></head><body><!--1. 内存溢出* 一种程序运行出现的错误* 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误2. 内存泄露* 占用的内存没有及时释放* 内存泄露积累多了就容易导致内存溢出* 常见的内存泄露:* 意外的全局变量* 没有及时清理的计时器或回调函数* 闭包--><script type="text/javascript">// 1. 内存溢出var obj = {}for (var i = 0; i < 10000; i++) {obj[i] = new Array(10000000)console.log('-----')}// 2. 内存泄露// 意外的全局变量function fn() {a = new Array(10000000)console.log(a)}fn()// 没有及时清理的计时器或回调函数var intervalId = setInterval(function () { //启动循环定时器后不清理console.log('----')}, 1000)// clearInterval(intervalId)// 闭包function fn1() {var a = 4function fn2() {console.log(++a)}return fn2}var f = fn1()f()// f = null</script></body></html>
———————-函数高级
01原型与原型链
待看的参考文章:https://www.cnblogs.com/wangfupeng1988/p/3977924.html 王福朋
01.函数的原型
- 函数的prototype属性(图)
- 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
- 原型对象中有一个属性constructor, 它指向函数对象(构造函数与原型对象相互引用)
创建函数定义的同时 函数内部会默认执行一条语句
- this.prototype = {} 此处的this指向的是函数对象
- 此条语句添加了一条属性 prototype
- 给原型对象添加属性(一般都是方法)
默认的方法都为空值(数据) 只包含一个prototype和一个construct属性
作用: 函数的所有实例对象自动拥有原型中的属性(方法)
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_原型(prototype)</title></head><body><!--1. 函数的prototype属性(图)* 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)* 原型对象中有一个属性constructor, 它指向函数对象2. 给原型对象添加属性(一般都是方法)* 作用: 函数的所有实例对象自动拥有原型中的属性(方法)--><script type="text/javascript">// 1.每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)console.log(Date.prototype, typeof Date.prototype)//打印 object+许多方法 "obejct"function Fun () {//alt + shift +r(重命名rename)}console.log(Fun.prototype) // 1.1 默认指向一个Object空对象(没有我们的属性)// 原型对象中有一个属性constructor, 它指向函数对象console.log(Date.prototype.constructor===Date)//trueconsole.log(Fun.prototype.constructor===Fun)//true//给原型对象添加属性(一般是方法) ===>实例对象可以访问Fun.prototype.test = function () {console.log('test()')}var fun = new Fun()fun.test()</script></body></html>
02.显式原型(类似类)与隐式原型(实例对象)
- 每个函数function都有一个prototype,即显式原型(属性)
- 每个实例对象都有一个proto,可称为隐式原型(属性)
对象的隐式原型的值为其对应构造函数的显式原型的值 存储的是同一个对象地址
- 创建实例对象时默认执行这一条函数 this.protope=Fn.prototype
- 构造函数的显式原型对象先产生 实例的隐式原型对象后产生
内存结构(图)
- 总结:
- 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
- 对象的proto属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
- 程序员能直接操作显式原型, 但不能直接操作隐式原型 (ES6之前)
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_显式原型与隐式原型</title></head><body><!--1. 每个函数function都有一个prototype,即显式原型(属性)2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)3. 对象的隐式原型的值为其对应构造函数的显式原型的值4. 内存结构(图)5. 总结:* 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象* 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值* 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)--><script type="text/javascript">//定义构造函数function Fn() { // 内部语句: this.prototype = {}}// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象console.log(Fn.prototype)// 2. 每个实例对象都有一个__proto__,可称为隐式原型//创建实例对象var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototypeconsole.log(fn.__proto__)// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值console.log(Fn.prototype===fn.__proto__) // true//给显式原型添加方法 不能直接操作隐式原型(ES6之前)Fn.prototype.test = function () {console.log('test()')}//通过实例调用原型的方法fn.test()//对象先找内部方法 没有再从隐式原型中寻找方法</script></body></html>
03.原型链
- 原型链(图解) 本质上是用于查找实例对象的属性 实际上是由隐形原型组成的链
访问一个对象的属性时,
- 先在自身属性中查找,找到返回
- 如果没有, 再沿着proto这条链向上查找, 找到返回
- 如果最终没找到, 返回undefined
- 别名: 隐式原型链
- 作用: 查找对象的属性(方法)
- 注意 对实例对象的属性赋值不会调用原型链 只有查找属性时会调用原型链
构造函数/原型/实体对象的关系(图解)

构造函数/原型/实体对象的关系2(图解)
<!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">// console.log(Object)//console.log(Object.prototype)console.log(Object.prototype.__proto__)function Fn() {this.test1 = function () {console.log('test1()')}}console.log(Fn.prototype)Fn.prototype.test2 = function () {console.log('test2()')}var fn = new Fn()fn.test1()fn.test2()console.log(fn.toString())console.log(fn.test3)// fn.test3() //test3() is not a function/*1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)*/console.log(Fn.prototype instanceof Object) // trueconsole.log(Object.prototype instanceof Object) // falseconsole.log(Function.prototype instanceof Object) // true/*2. 所有函数都是Function的实例(包含Function)*/console.log(Function.__proto__===Function.prototype)/*3. Object的原型对象是原型链尽头*/console.log(Object.prototype.__proto__) // null</script></body></html>
04.原型链的属性问题
<!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.a = 'xxx'var fn1 = new Fn()console.log(fn1.a, fn1)var fn2 = new Fn()fn2.a = 'yyy'console.log(fn1.a, fn2.a, fn2)function Person(name, age) {this.name = namethis.age = age}Person.prototype.setName = function (name) {this.name = name}var p1 = new Person('Tom', 12)p1.setName('Bob')console.log(p1)var p2 = new Person('Jack', 12)p2.setName('Cat')console.log(p2)console.log(p1.__proto__===p2.__proto__) // true 都指向构造函数的宣誓原型</script></body></html>
05.探索instanceof
- instanceof是如何判断的?
- 表达式: A instanceof B
- 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
- Function是通过new自己产生的实例
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_探索instanceof</title></head><body><!--1. instanceof是如何判断的?* 表达式: A instanceof B* 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false2. Function是通过new自己产生的实例--><script type="text/javascript">/*案例1*/function Foo() { }var f1 = new Foo()console.log(f1 instanceof Foo) // trueconsole.log(f1 instanceof Object) // true/*案例2*/console.log(Object instanceof Function) // trueconsole.log(Object instanceof Object) // trueconsole.log(Function instanceof Function) // trueconsole.log(Function instanceof Object) // truefunction Foo() {}console.log(Object instanceof Foo) // false</script></body></html>
06.面试题
测试题1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>06_面试题</title></head><body><script type="text/javascript">/*测试题1*/function A () {}A.prototype.n = 1var b = new A()A.prototype = { //此刻修改的是函数的显示原型对象 原来的实例依旧引用的之前创建的空对象 没有被销毁 只是后续定义的实例链接的对象不再一致n: 2,m: 3}var c = new A()console.log(b.n, b.m, c.n, c.m) //1 undefine 2 3/*测试题2*/function F (){}Object.prototype.a = function(){console.log('a()')}Function.prototype.b = function(){console.log('b()')}var f = new F()f.a()// f.b()F.a()F.b()console.log(f)console.log(Object.prototype)console.log(Function.prototype)</script></body></html>
02.执行上下文与执行上下文栈
01.变量提升与函数提升
- 变量声明提升
- 通过var定义(声明)的变量, 在定义语句之前就可以访问到
- 值: undefined
- 函数声明提升
- 通过function声明的函数, 在之前就可以直接调用
- 值: 函数定义(对象)
- 问题: 变量提升和函数提升是如何产生的?
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_变量提升与函数提升</title></head><body><!--1. 变量声明提升* 通过var定义(声明)的变量, 在定义语句之前就可以访问到* 值: undefined2. 函数声明提升* 通过function声明的函数, 在之前就可以直接调用* 值: 函数定义(对象)3. 问题: 变量提升和函数提升是如何产生的?--><script type="text/javascript">console.log('-----')/*面试题 : 输出 undefined*/var a = 3function fn () {console.log(a)//优先寻找函数内部作用域var a = 4//变量提升 但是赋值操作还未进行}fn()console.log(b) //undefined 变量提升fn2() //可调用 函数提升(必须使用函数申明的方式)// fn3() //不能 变量提升var b = 3function fn2() {console.log('fn2()')}var fn3 = function () {console.log('fn3()')}</script></body></html>
02.执行上下文
- 代码分类(位置)
- 全局代码
- 函数(局部)代码
- 全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理
- var定义的全局变量==>undefined, 添加为window的属性
- function声明的全局函数==>赋值(fun), 添加为window的方法
- this==>赋值(window)
- 开始执行全局代码
- 函数执行上下文
- 创建的时机: 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
对局部数据进行预处理
- 形参变量==>赋值(实参)==>添加为执行上下文的属性
- arguments==>赋值(实参列表), 添加为执行上下文的属性
- var定义的局部变量==>undefined, 添加为执行上下文的属性
- function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
- this==>赋值(调用函数的对象)
- 开始执行函数体代码
<!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)window.a2()console.log(this)var a1 = 3function a2() {console.log('a2()')}console.log(a1)</script></body></html>
03.执行上下文栈
<!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">var a = 10var bar = function (x) {var b = 5foo(x + b)}var foo = function (y) {var c = 5console.log(a + c + y)}bar(10)// bar(10)</script></body></html>
04.执行上下文栈面试题
面试题1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>04_执行上下文栈2</title></head><body><!--1. 依次输出什么? 注意这里是函数的嵌套 内部函数的作用域和外部的不同 传入的参数和全局变量一致,传入的参数作为中间变量gb: undefinedfb: 1fb: 2fb: 3fe: 3fe: 2fe: 1ge: 12. 整个过程中产生了几个执行上下文? 5 1个windows 4个函数--><script type="text/javascript">console.log('gb: '+ i)var i = 1foo(1)function foo(i) {if (i == 4) {return}console.log('fb:' + i)foo(i + 1) //递归调用: 在函数内部调用自己console.log('fe:' + i)}console.log('ge: ' + i)</script></body></html>
面试题2
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_面试题</title><link rel="stylesheet" href="xxx.css"><style></style></head><body><div style=""></div><script type="text/javascript">/*测试题1: 先执行变量提升, 再执行函数提升 另一种说法 函数申明会覆盖变量申明*/function a() {}var aconsole.log(typeof a) // 'function'/*测试题2: 即使在if语句中的变量申明 也会进行变量提升 所以这里的 b in window为true if语句进不去*/if (!(b in window)) {var b = 1}console.log(b) // undefined/*测试题3:*/var c = 1function c(c) {console.log(c)var c = 3}c(2) // 报错</script></body></html>
03.作用域与作用域链
01.作用域
- 理解
- 就是一块”地盘”, 一个代码段所在的区域
- 它是静态的(相对于上下文对象), 在编写代码时就确定了
- 分类
- 全局作用域
- 函数作用域
- 没有块作用域(ES6有了) 一个大括号代表一个块作用域
- 作用
- 隔离变量,不同作用域下同名变量不会有冲突
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_作用域</title></head><body><!--1. 理解* 就是一块"地盘", 一个代码段所在的区域* 它是静态的(相对于上下文对象), 在编写代码时就确定了2. 分类* 全局作用域* 函数作用域* 没有块作用域(ES6有了)3. 作用* 隔离变量,不同作用域下同名变量不会有冲突--><script type="text/javascript">/* //没块作用域if(true) {var c = 3}console.log(c)*/var a = 10,b = 20function fn(x) {var a = 100,c = 300;console.log('fn()', a, b, c, x)//fn() 100 20 300 10function bar(x) {var a = 1000,d = 400console.log('bar()', a, b, c, d, x)//'bar()'1000 20,300,400,100}bar(100)bar(200)}fn(10)</script></body></html>
02.作用域与全局上下文的区别与联系
区别1
- 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
- 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
- 函数执行上下文是在调用函数时, 函数体代码执行之前创建
区别2
- 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
- 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
联系
- 执行上下文(对象)是从属于所在的作用域
- 全局上下文环境==>全局作用域
- 函数上下文环境==>对应的函数使用域

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_作用域与执行上下文</title></head><body><!--1. 区别1* 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时* 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建* 函数执行上下文是在调用函数时, 函数体代码执行之前创建2. 区别2* 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化* 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放3. 联系* 执行上下文(对象)是从属于所在的作用域* 全局上下文环境==>全局作用域* 函数上下文环境==>对应的函数使用域--><script type="text/javascript">var a = 10,b = 20function fn(x) {var a = 100,c = 300;console.log('fn()', a, b, c, x)function bar(x) {var a = 1000,d = 400console.log('bar()', a, b, c, d, x)}bar(100)bar(200)}fn(10)</script></body></html>
03.作用域链
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>03_作用域链</title></head><body><!--1. 理解* 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)* 查找变量时就是沿着作用域链来查找的2. 查找一个变量的查找规则* 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2* 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3* 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常--><script type="text/javascript">var a = 1function fn1() {var b = 2function fn2() {var c = 3console.log(c)console.log(b)console.log(a)console.log(d)}fn2()}fn1()</script></body></html>
04.面试题
面试题1
<!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);}function show(f) {var x = 20;f();}show(fn);//作用域产生与代码的编写方式 而不是函数的调用方式 在fn()中找变量x时其 其会直接访问它上层的全局作用域 所以输出为10</script></body></html>
面试题2
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>04_作用域_面试题2</title></head><body><script type="text/javascript">var fn = function () {console.log(fn)}fn()var obj = {fn2: function () {console.log(fn2)//console.log(this.fn2)}}obj.fn2()</script></body></html>
04.闭包
01.引入
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>00_引入</title></head><body><button>测试1</button><button>测试2</button><button>测试3</button><!--需求: 点击某个按钮, 提示"点击的是第n个按钮"--><script type="text/javascript">var btns = document.getElementsByTagName('button')//遍历加监听/*for (var i = 0,length=btns.length; i < length; i++) {var btn = btns[i]btn.onclick = function () {alert('第'+(i+1)+'个')}}*//*for (var i = 0,length=btns.length; i < length; i++) {var btn = btns[i]//将btn所对应的下标保存在btn上btn.index = ibtn.onclick = function () {alert('第'+(this.index+1)+'个')}}*///利用闭包for (var i = 0,length=btns.length; i < length; i++) {(function (j) {var btn = btns[j]btn.onclick = function () {alert('第'+(j+1)+'个')}})(i)}</script></body></html>
02.理解闭包
- 如何产生闭包?
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
- 闭包到底是什么?
- 使用chrome调试查看
- 理解一: 闭包是嵌套的内部函数(绝大部分人)
- 理解二: 包含被引用变量(函数)的对象(极少数人)
- 注意: 闭包存在于嵌套的内部函数中
- 产生闭包的条件?
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_理解闭包</title></head><body><!--1. 如何产生闭包?* 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包2. 闭包到底是什么?* 使用chrome调试查看* 理解一: 闭包是嵌套的内部函数(绝大部分人)* 理解二: 包含被引用变量(函数)的对象(极少数人)* 注意: 闭包存在于嵌套的内部函数中3. 产生闭包的条件?* 函数嵌套* 内部函数引用了外部函数的数据(变量/函数)--><script type="text/javascript">function fn1 () {var a = 2var b = 'abc'function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)console.log(a)}// fn2()}fn1()function fun1() {var a = 3var fun2 = function () {console.log(a)}}fun1()</script></body></html>
03.常见的闭包
- 将函数作为另一个函数的返回值
- 将函数作为实参传递给另一个函数调用
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_常见的闭包</title></head><body><!--1. 将函数作为另一个函数的返回值2. 将函数作为实参传递给另一个函数调用--><script type="text/javascript">// 1. 将函数作为另一个函数的返回值function fn1() {var a = 2function fn2() {a++console.log(a)}return fn2}var f = fn1()f() // 3f() // 4// 2. 将函数作为实参传递给另一个函数调用function showDelay(msg, time) {setTimeout(function () {alert(msg)}, time)}showDelay('atguigu', 2000)</script></body></html>
再接上一条3*.执行第二个fn,也就是再执行一次fn2函数,此时a在自己中找不到,就去外部函数中找,外部栈中由于第2步变为3存在里面,这时fn2中a就为3,这也解释了此时闭包中a=3,然后a自增变4
这里有人会说fn1不是第一次执行完之后里面的变量全部死亡了吗,怎么执行fn2的时候还能在fn1中找到a,这里是由于fn1执行的时候也函数提升了,此时a相当于在全局作用域栈中f

04.闭包的作用
- 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
- 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在
- 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它
fn1()执行完由于是函数释放成为垃圾对象,当f=fn1时由于是赋值操作把地址赋值给了所以现在是f和fn1都指向一个内存空间,所以fn1执行完不能释放因为f还指向这个内存空间
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>03_闭包的作用</title></head><body><!--1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)问题:1. 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它--><script type="text/javascript">function fn1() {var a = 2function fn2() {a++console.log(a)// return a}function fn3() {a--console.log(a)}return fn3}var f = fn1()f() // 1f() // 0</script></body></html>
05.闭包的生命周期
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>04_闭包的生命周期</title></head><body><!--1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)2. 死亡: 在嵌套的内部函数成为垃圾对象时--><script type="text/javascript">function fn1() {//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)var a = 2function fn2 () {a++console.log(a)}return fn2}var f = fn1()f() // 3f() // 4f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)</script></body></html>
06.闭包的应用 自定义模板
模板1
function myModule() {//私有数据var msg = 'My atguigu'//操作数据的函数function doSomething() {console.log('doSomething() '+msg.toUpperCase())}function doOtherthing () {console.log('doOtherthing() '+msg.toLowerCase())}//向外暴露对象(给外部使用的方法)return {doSomething: doSomething,doOtherthing: doOtherthing}}
模板2
(function () {//私有数据var msg = 'My atguigu'//操作数据的函数function doSomething() {console.log('doSomething() '+msg.toUpperCase())}function doOtherthing () {console.log('doOtherthing() '+msg.toLowerCase())}//向外暴露对象(给外部使用的方法)window.myModule2 = {doSomething: doSomething,doOtherthing: doOtherthing}})()
调用1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_闭包的应用_自定义JS模块</title></head><body><!--闭包的应用2 : 定义JS模块* 具有特定功能的js文件* 将所有的数据和功能都封装在一个函数内部(私有的)* 只向外暴露一个包信n个方法的对象或函数* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能--><script type="text/javascript" src="myModule.js"></script><script type="text/javascript">var module = myModule()module.doSomething()module.doOtherthing()</script></body></html>
调用2
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_闭包的应用_自定义JS模块2</title></head><body><!--闭包的应用2 : 定义JS模块* 具有特定功能的js文件* 将所有的数据和功能都封装在一个函数内部(私有的)* 只向外暴露一个包信n个方法的对象或函数* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能--><script type="text/javascript" src="myModule2.js"></script><script type="text/javascript">myModule2.doSomething()myModule2.doOtherthing()</script></body></html>
07.闭包的缺点及解决
- 缺点
- 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
- 容易造成内存泄露
- 解决
- 能不用闭包就不用
- 及时释放
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>06_闭包的缺点及解决</title></head><body><!--1. 缺点* 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长* 容易造成内存泄露2. 解决* 能不用闭包就不用* 及时释放--><script type="text/javascript">function fn1() {var arr = new Array[100000]function fn2() {console.log(arr.length)}return fn2}var f = fn1()f()f = null //让内部函数成为垃圾对象-->回收闭包</script></body></html>
08.闭包面试题1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>07_面试题1</title></head><body><script type="text/javascript">//代码片段一var name = "The Window";var object = {name : "My Object",getNameFunc : function(){return function(){return this.name;};}};alert(object.getNameFunc()()); //? the window 执行函数的对象是windows//代码片段二var name2 = "The Window";var object2 = {name2 : "My Object",getNameFunc : function(){var that = this;return function(){return that.name2;};}};alert(object2.getNameFunc()()); //? my object 闭包(内部函数引用外部函数变量) that指向的是Object</script></body></html>
09.闭包面试题2
没讲
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>07_面试题2</title></head><body><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(2)a.fun(3)//undefined,0,0,0var b = fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2var c = fun(0).fun(1)c.fun(2)c.fun(3)//undefined,0,1,1</script></body></html>
———————-对象高级
01.对象创建模式
01._Object构造函数模式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_Object构造函数模式</title></head><body><!--方式一: Object构造函数模式* 套路: 先创建空Object对象, 再动态添加属性/方法* 适用场景: 起始时不确定对象内部数据* 问题: 语句太多--><script type="text/javascript">/*一个人: name:"Tom", age: 12*/// 先创建空Object对象var p = new Object()//var p = {}p = {} //此时内部数据是不确定的// 再动态添加属性/方法p.name = 'Tom'p.age = 12p.setName = function (name) {this.name = name}//测试console.log(p.name, p.age)//Tomp.setName('Bob')console.log(p.name, p.age)//Bob 12</script></body></html>
02._对象字面量的方式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_对象字面量</title></head><body><!--方式二: 对象字面量模式* 套路: 使用{}创建对象, 同时指定属性/方法* 适用场景: 起始时对象内部数据是确定的* 问题: 如果创建多个对象, 有重复代码--><script type="text/javascript">var p = {name: 'Tom',age: 12,setName: function (name) {this.name = name}}//测试console.log(p.name, p.age)p.setName('JACK')console.log(p.name, p.age)var p2 = { //如果创建多个对象代码很重复name: 'Bob',age: 13,setName: function (name) {this.name = name}}</script></body></html>
03.工厂模式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>03_工厂模式</title></head><body><!--方式三: 工厂模式* 套路: 通过工厂函数动态创建对象并返回* 适用场景: 需要创建多个对象* 问题: 对象没有一个具体的类型, 都是Object类型--><script type="text/javascript">function createPerson(name, age) { //返回一个对象的函数===>工厂函数var obj = {name: name,age: age,setName: function (name) {this.name = name}}return obj}// 创建2个人var p1 = createPerson('Tom', 12)var p2 = createPerson('Bob', 13)// p1/p2是Object类型function createStudent(name, price) {var obj = {name: name,price: price}return obj}var s = createStudent('张三', 12000)// s也是Object</script></body></html>
04._自定义构造函数模式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>04_自定义构造函数模式</title></head><body><!--方式四: 自定义构造函数模式* 套路: 自定义构造函数, 通过new创建对象* 适用场景: 需要创建多个类型确定的对象* 问题: 每个对象都有相同的数据, 浪费内存--><script type="text/javascript">//定义类型function Person(name, age) {this.name = namethis.age = agethis.setName = function (name) {this.name = name}}var p1 = new Person('Tom', 12)p1.setName('Jack')console.log(p1.name, p1.age)console.log(p1 instanceof Person)function Student (name, price) {this.name = namethis.price = price}var s = new Student('Bob', 13000)console.log(s instanceof Student)var p2 = new Person('JACK', 23)console.log(p1, p2)</script></body></html>
05_构造函数+原型的组合模式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_构造函数+原型的组合模式</title></head><body><!--方式六: 构造函数+原型的组合模式* 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上* 适用场景: 需要创建多个类型确定的对象--><script type="text/javascript">function Person(name, age) { //在构造函数中只初始化一般函数this.name = namethis.age = age}Person.prototype.setName = function (name) {this.name = name}var p1 = new Person('Tom', 23)var p2 = new Person('Jack', 24)console.log(p1, p2)</script></body></html>
02.继承模式
01.原型链继承
<!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.supProp = 'Supper property'}Supper.prototype.showSupperProp = function () {console.log(this.supProp)}//子类型function Sub() {this.subProp = 'Sub property'}// 子类型的原型为父类型的一个实例对象Sub.prototype = new Supper()// 让子类型的原型的constructor指向子类型Sub.prototype.constructor = SubSub.prototype.showSubProp = function () {console.log(this.subProp)}var sub = new Sub()sub.showSupperProp()// sub.toString()sub.showSubProp()console.log(sub) // Sub</script></body></html>

02.借用构造函数继承
<!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 = namethis.age = age}function Student(name, age, price) {Person.call(this, name, age) // 相当于: this.Person(name, age)/*this.name = namethis.age = age*/this.price = price}var s = new Student('Tom', 20, 14000)console.log(s.name, s.age, s.price)</script></body></html>
03.组合继承
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>03_组合继承</title></head><body><!--方式3: 原型链+借用构造函数的组合继承1. 利用原型链实现对父类型对象的方法继承2. 利用super()借用父类型构建函数初始化相同属性--><script type="text/javascript">function Person(name, age) {this.name = namethis.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 //修正constructor属性Student.prototype.setPrice = function (price) {this.price = price}var s = new Student('Tom', 24, 15000)s.setName('Bob')s.setPrice(16000)console.log(s.name, s.age, s.price)</script></body></html>
———————-线程机制与事件机制
01_进程与线程
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>01_进程与线程</title></head><body><!--1. 进程:程序的一次执行, 它占有一片独有的内存空间2. 线程: CPU的基本调度单位, 是程序执行的一个完整流程3. 进程与线程* 一个进程中一般至少有一个运行的线程: 主线程* 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的* 一个进程内的数据可以供其中的多个线程直接共享* 多个进程之间的数据是不能直接共享的4. 浏览器运行是单进程还是多进程?* 有的是单进程* firefox* 老版IE* 有的是多进程* chrome* 新版IE5. 如何查看浏览器是否是多进程运行的呢?* 任务管理器==>进程6. 浏览器运行是单线程还是多线程?* 都是多线程运行的--></body></html>
02_浏览器内核
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>02_浏览器内核</title></head><body><!--1. 什么是浏览器内核?* 支持浏览器运行的最核心的程序2. 不同的浏览器可能不太一样* Chrome, Safari: webkit* firefox: Gecko* IE: Trident* 360,搜狗等国内浏览器: Trident + webkit3. 内核由很多模块组成* html,css文档解析模块 : 负责页面文本的解析* dom/css模块 : 负责dom/css在内存中的相关处理* 布局和渲染模块 : 负责页面的布局和效果的绘制* 布局和渲染模块 : 负责页面的布局和效果的绘制* 定时器模块 : 负责定时器的管理* 网络请求模块 : 负责服务器请求(常规/Ajax)* 事件响应模块 : 负责事件的管理--></body></html>
03_定时器引发的思考
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>03_定时器引发的思考</title></head><body><button id="btn">启动定时器</button><!--1. 定时器真是定时执行的吗?* 定时器并不能保证真正定时执行* 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)2. 定时器回调函数是在分线程执行的吗?* 在主线程执行的, js是单线程的3. 定时器是如何实现的?* 事件循环模型(后面讲)--><script type="text/javascript">document.getElementById('btn').onclick = function () {var start = Date.now()console.log('启动定时器前...')setTimeout(function () {console.log('定时器执行了', Date.now()-start)}, 200)console.log('启动定时器后...')// 做一个长时间的工作for (var i = 0; i < 1000000000; i++) {}}</script></body></html>
04_JS是单线程的
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>04_JS是单线程的</title></head><body><!--1. 如何证明js执行是单线程的?* setTimeout()的回调函数是在主线程执行的* 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行2. 为什么js要用单线程模式, 而不用多线程模式?* JavaScript的单线程,与它的用途有关。* 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。* 这决定了它只能是单线程,否则会带来很复杂的同步问题3. 代码的分类:* 初始化代码* 回调代码4. js引擎执行代码的基本流程* 先执行初始化代码: 包含一些特别的代码 回调函数(异步执行)* 设置定时器* 绑定事件监听* 发送ajax请求* 后面在某个时刻才会执行回调代码--><script type="text/javascript">setTimeout(function () {console.log('timeout 2222')alert('22222222')}, 2000)setTimeout(function () {console.log('timeout 1111')alert('1111111')}, 1000)setTimeout(function () {console.log('timeout() 00000')}, 0)function fn() {console.log('fn()')}fn()console.log('alert()之前')alert('------') //暂停当前主线程的执行, 同时暂停计时, 点击确定后, 恢复程序执行和计时console.log('alert()之后')</script></body></html>
05_事件循环模型
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>05_事件循环模型</title></head><body><button id="btn">测试</button><!--1. 所有代码分类* 初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码* 回调执行代码(异步代码): 处理回调逻辑2. js引擎执行代码的基本流程:* 初始化代码===>回调代码3. 模型的2个重要组成部分:* 事件(定时器/DOM事件/Ajax)管理模块* 回调队列4. 模型的运转流程* 执行初始化代码, 将事件回调函数交给对应模块管理* 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中* 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行--><script type="text/javascript">function fn1() {console.log('fn1()')}fn1()document.getElementById('btn').onclick = function () {console.log('点击了btn')}setTimeout(function () {console.log('定时器执行了')}, 2000)function fn2() {console.log('fn2()')}fn2()</script></body></html>
06Web Workers测试
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>06_Web Workers_测试</title></head><body><!--1. H5规范提供了js分线程的实现, 取名为: Web Workers2. 相关API* Worker: 构造函数, 加载分线程执行的js文件* Worker.prototype.onmessage: 用于接收另一个线程的回调函数* Worker.prototype.postM ssage: 向另一个线程发送消息3. 不足* worker内代码不能操作DOM(更新UI)* 不能跨域加载JS* 不是每个浏览器都支持这个新特性--><input type="text" placeholder="数值" id="number"><button id="btn">计算</button><script type="text/javascript">// 1 1 2 3 5 8 f(n) = f(n-1) + f(n-2)function fibonacci(n) {return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //递归调用}// console.log(fibonacci(7))var input = document.getElementById('number')document.getElementById('btn').onclick = function () {var number = input.valuevar result = fibonacci(number)alert(result)}</script></body></html>
06Web Workers测试2
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>06_Web Workers_测试</title></head><body><!--1. H5规范提供了js分线程的实现, 取名为: Web Workers2. 相关API* Worker: 构造函数, 加载分线程执行的js文件* Worker.prototype.onmessage: 用于接收另一个线程的回调函数* Worker.prototype.postMessage: 向另一个线程发送消息3. 不足* worker内代码不能操作DOM(更新UI)* 不能跨域加载JS* 不是每个浏览器都支持这个新特性--><input type="text" placeholder="数值" 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) {console.log('主线程接收分线程返回的数据: '+event.data)alert(event.data)}// 向分线程发送消息worker.postMessage(number)console.log('主线程向分线程发送数据: '+number)}// console.log(this) // window</script></body></html>
.png)
function fibonacci(n) {return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //递归调用}console.log(this)this.onmessage = function (event) {var number = event.dataconsole.log('分线程接收到主线程发送的数据: '+number)//计算var result = fibonacci(number)postMessage(result)console.log('分线程向主线程返回数据: '+result)// alert(result) alert是window的方法, 在分线程不能调用// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面}

