JavaScript高级进阶学习笔记
一、基础总结深入
1、数据类型一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01_数据类型</title>
</head>
<body>
<!--
1. 分类
* 基本(值)类型
* String: 任意字符串
* Number: 任意的数字
* boolean: true/false
* undefined: undefined
* null: null
* 对象(引用)类型
* Object: 任意对象
* Function: 一种特别的对象(可以执行)
* Array: 一种特别的对象(数值下标, 内部数据是有序的)
2. 判断
* typeof:
* 可以判断: undefined/ 数值 / 字符串 / 布尔值 / function
* 不能判断: null与object object与array
* instanceof:
* 判断对象的具体类型
* ===
* 可以判断: undefined, null
-->
<script type="text/javascript">
//1. 基本
// typeof返回数据类型的字符串表达
var a;
console.log(a, typeof a, typeof a === 'undefined', a === undefined); // undefined 'undefined' true true
console.log(undefined === 'undefined');
a = 4;
console.log(typeof a === 'number');
a = 'atguigu';
console.log(typeof a === 'string');
a = true;
console.log(typeof a === 'boolean');
a = null;
console.log(typeof a, a === null); // 'object'
console.log('-----------------');
//2. 对象
var b1 = {
b2: [1, 'abc', console.log],
b3: function() {
console.log('b3');
return function() {
return 'xfzhang';
}
}
}
console.log(b1 instanceof Object, b1 instanceof Array); // true false
console.log(b1.b2 instanceof Array, b1.b2 instanceof Object); // true true
console.log(b1.b3 instanceof Function, b1.b3 instanceof Object); // true true
console.log(typeof b1.b2, '-------'); // 'object'
console.log(typeof b1.b3 === 'function'); // true
console.log(typeof b1.b2[2] === 'function');
b1.b2[2](4);
console.log(b1.b3()());
</script>
</body>
</html>
2、数据类型二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01_相关问题</title>
</head>
<body>
<!--
1. undefined与null的区别?
* undefined代表定义未赋值
* null定义并赋值了, 只是值为null
2. 什么时候给变量赋值为null呢?
* 初始赋值, 表明将要赋值为对象
* 结束前, 让对象成为垃圾对象(被垃圾回收器回收)
3. 严格区别变量类型与数据类型?
* 数据的类型
* 基本类型
* 对象类型
* 变量的类型(变量内存值的类型)
* 基本类型: 保存就是基本类型的数据
* 引用类型: 保存的是地址值
-->
<script type="text/javascript">
// 实例: 实例对象
// 类型: 类型对象
function Person(name, age) {
// 构造函数 类型
this.name = name;
this.age = age;
}
var p = new Person('tom', 12); // 根据类型创建的实例对象
// Person('jack', 12);
// 1. undefined与null的区别?
var a;
console.log(a);// undefined
a = null;
console.log(a); // null
//起始
var b = null; // 初始赋值为null, 表明将要赋值为对象
//确定对象就赋值
b = ['atguigu', 12];
//最后
b = null; // 让b指向的对象成为垃圾对象(被垃圾回收器回收)
// b = 2
var c = function() {
}
console.log(typeof c); // 'function'
</script>
</body>
</html>
3、数据、变量、内存
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02_数据_变量_内存</title>
</head>
<body>
<!--
1. 什么是数据?
* 存储在内存中代表特定信息的'东东', 本质上是0101...
* 数据的特点: 可传递, 可运算
* 一切皆数据
* 内存中所有操作的目标: 数据
* 算术运算
* 逻辑运算
* 赋值
* 运行函数
2. 什么是内存?
* 内存条通电后产生的可储存数据的空间(临时的)
* 内存产生和死亡: 内存条(电路版)==>通电==>产生内存空间==>存储数据==>处理数据==>断电==>内存空间和数据都消失
* 一块小内存的2个数据
* 内部存储的数据
* 地址值
* 内存分类
* 栈: 全局变量/局部变量
* 堆: 对象
3. 什么是变量?
* 可变化的量, 由变量名和变量值组成
* 每个变量都对应的一块小内存, 变量名用来查找对应的内存, 变量值就是内存中保存的数据
4. 内存,数据, 变量三者之间的关系
* 内存用来存储数据的空间
* 变量是内存的标识
-->
<script type="text/javascript">
var age = 18;
console.log(age);
var obj = {
name: 'Tom'
}
console.log(obj.name);
function fn() {
var obj = {
name: 'Tom'
}
}
var a = 3;
var b = a + 2;
</script>
</body>
</html>
3.1、关于赋值与内存的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>关于赋值和内存的问题</title>
</head>
<body>
<!--
问题: var a = xxx, a内存中到底保存的是什么?
* xxx是基本数据, 保存的就是这个数据
* xxx是对象, 保存的是对象的地址值
* xxx是一个变量, 保存的xxx的内存内容(可能是基本数据, 也可能是地址值)
-->
<script type="text/javascript">
var a = 3;
a = function() {
}
var b = 'abc'
a = b;
b = {
};
a = b;
</script>
</body>
</html>
3.2、关于引用变量赋值问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02_关于引用变量赋值问题</title>
</head>
<body>
<!--
关于引用变量赋值问题
* 2个引用变量指向同一个对象, 通过一个变量修改对象内部数据, 另一个变量看到的是修改之后的数据
* 2个引用变量指向同一个对象, 让其中一个引用变量指向另一个对象, 另一引用变量依然指向前一个对象
-->
<script type="text/javascript">
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
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
</script>
</body>
</html>
3.3、关于数据传递问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02_关于数据传递问题</title>
</head>
<body>
<!--
问题: 在js调用函数时传递变量参数时, 是值传递还是引用传递
* 理解1: 都是值(基本/地址值)传递
* 理解2: 可能是值传递, 也可能是引用传递(地址值)
-->
<script type="text/javascript">
var a = 3;
function fn(a) {
a = a + 1;
}
fn(a);
console.log(a); //3
function fn2(obj) {
console.log(obj.name);
}
var obj = {
name: 'Tom'
}
fn2(obj);
</script>
</body>
</html>
3.4、JS引擎如何管理内存
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02_内存管理</title>
</head>
<body>
<!--
问题: JS引擎如何管理内存?
1. 内存生命周期
* 分配小内存空间, 得到它的使用权
* 存储数据, 可以反复进行操作
* 释放小内存空间
2. 释放内存
* 局部变量: 函数执行完自动释放
* 对象: 成为垃圾对象==>垃圾回收器回收
-->
<script type="text/javascript">
var a = 3;
var obj = {
};
obj = undefined;
function fn() {
var b = {
};
}
fn(); // b是自动释放, b所指向的对象是在后面的某个时刻由垃圾回收器回收
</script>
</body>
</html>
4、对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03_对象</title>
</head>
<body>
<!--
1. 什么是对象?
* 多个数据的封装体
* 用来保存多个数据的容器
* 一个对象代表现实中的一个事物
2. 为什么要用对象?
* 统一管理多个数据
3. 对象的组成
* 属性: 属性名(字符串)和属性值(任意)组成
* 方法: 一种特别的属性(属性值是函数)
4. 如何访问对象内部数据?
* .属性名: 编码简单, 有时不能用
* ['属性名']: 编码麻烦, 能通用
-->
<script type="text/javascript">
var p = {
name: 'Tom',
age: 12,
setName: function(name) {
this.name = name;
},
setAge: function(age) {
this.age = age;
}
};
p.setName('Bob');
p['setAge'](23);
console.log(p.name, p['age']);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03_相关问题</title>
</head>
<body>
<!--
问题: 什么时候必须使用['属性名']的方式?
1. 属性名包含特殊字符: - 空格
2. 属性名不确定
-->
<script type="text/javascript">
var p = {
};
//1. 给p对象添加一个属性: content-type: text/json
// p.content-type = 'text/json'; //不能用
p['content-type'] = 'text/json';
console.log(p['content-type']);
//2. 属性名不确定
var propName = 'myAge';
var value = 18;
// p.propName = value //不能用
p[propName] = value;
console.log(p[propName]);
</script>
</body>
</html>
5、函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>04_函数</title>
</head>
<body>
<!--
1. 什么是函数?
* 实现特定功能的n条语句的封装体
* 只有函数是可以执行的, 其它类型的数据不能执行
2. 为什么要用函数?
* 提高代码复用
* 便于阅读交流
3. 如何定义函数?
* 函数声明
* 表达式
4. 如何调用(执行)函数?
* test(): 直接调用
* obj.test(): 通过对象调用
* new test(): new调用
* test.call/apply(obj): 临时让test成为obj的方法进行调用
-->
<script type="text/javascript">
/*
编写程序实现以下功能需求:
1. 根据年龄输出对应的信息
2. 如果小于18, 输出: 未成年, 再等等!
3. 如果大于60, 输出: 算了吧!
4. 其它, 输出: 刚好!
*/
function showInfo(age) {
if (age < 18) {
console.log('未成年, 再等等!');
} else if (age > 60) {
console.log('算了吧!');
} else {
console.log('刚好!');
}
}
showInfo(17);
showInfo(20);
showInfo(65);
function fn1() {
//函数声明
console.log('fn1()');
}
var fn2 = function() {
//表达式
console.log('fn2()');
};
fn1();
fn2();
var obj = {
};
function test2() {
this.xxx = 'atguigu';
}
// obj.test2() 不能直接, 根本就没有
test2.call(obj); // obj.test2() // 可以让一个函数成为指定任意对象的方法进行调用
console.log(obj.xxx);
</script>
</body>
</html>
6、回调函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_回调函数</title>
</head>
<body>
<button id="btn">测试点击事件</button>
<!--
1. 什么函数才是回调函数?
1). 你定义的
2). 你没有调
3). 但最终它执行了(在某个时刻或某个条件下)
2. 常见的回调函数?
* dom事件回调函数 ==>发生事件的dom元素
* 定时器回调函数 ===>window
* ajax请求回调函数(后面讲)
* 生命周期回调函数(后面讲)
-->
<script type="text/javascript">
document.getElementById('btn').onclick = function() {
// dom事件回调函数
alert(this.innerHTML)
}
//定时器
// 超时定时器
// 循环定时器
setTimeout(function() {
// 定时器回调函数
alert('到点了' + this)
}, 2000)
/*var a = 3
alert(window.a)
window.b = 4
alert(b)*/
</script>
</body>
</html>
7、IIFE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>06_IIFE</title>
</head>
<body>
<!--
1. 理解
* 全称: Immediately-Invoked Function Expression
2. 作用
* 隐藏实现
* 不会污染外部(全局)命名空间
* 用它来编码js模块
-->
<script type="text/javascript">
(function() {
//匿名函数自调用
var a = 3;
console.log(a + 3);
})()
var a = 4;
console.log(a);
(function() {
var a = 1;
function test() {
console.log(++a);
}
window.$ = function() {
// 向外暴露一个全局函数
return {
test: test
}
}
})()
$().test() // 1. $是一个函数 2. $执行后返回的是一个对象
</script>
</body>
</html>
8、函数的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是谁? window
var p = new Person("yello"); //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();
</script>
</body>
</html>
9、小结
二、函数高级
1、原型与原型链
1、原型prototype
<!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">
// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
console.log(Date.prototype, typeof Date.prototype);
function Fun() {
}
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()');
}
var fun = new Fun();
fun.test();
</script>
</body>
</html>
2、显示原型与隐式原型
<!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.prototype
console.log(fn.__proto__);
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__) ;// true
//给原型添加方法
Fn.prototype.test = function() {
console.log('test()');
}
//通过实例调用原型的方法
fn.test();
</script>
</body>
</html>
3、原型链
<!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()
/*
1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
*/
console.log(Fn.prototype instanceof Object); // true
console.log(Object.prototype instanceof Object); // false
console.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>
4、原型链属性问题
<!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 = name;
this.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>
5、深入instanceof
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_探索instanceof</title>
</head>
<body>
<!--
1. instanceof是如何判断的?
* 表达式: A instanceof B
* 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
2. Function是通过new自己产生的实例
-->
<script type="text/javascript">
/*
案例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
</script>
</body>
</html>
6、小结
2、执行上下文与执行上下文栈
1、变量提升与函数提升
<!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">
console.log('-----');
/*
面试题 : 输出 undefined
*/
var a = 3;
function fn() {
console.log(a)
var a = 4
}
fn();
console.log(b) //undefined 变量提升
fn2(); //可调用 函数提升
// fn3() //不能 变量提升
var b = 3;
function fn2() {
console.log('fn2()');
}
var fn3 = function() {
console.log('fn3()');
}
</script>
</body>
</html>
2、执行上下文
<!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 = 3;
function a2() {
console.log('a2()');
}
console.log(a1);
//函数执行上下文
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);
</script>
</body>
</html>
3、执行上下文栈一
<!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 = 10
var bar = function(x) {
var b = 5;
foo(x + b);
}
var foo = function(y) {
var c = 5;
console.log(a + c + y);
}
bar(10);
// bar(10)
</script>
</body>
</html>
4、执行上下文栈二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>04_执行上下文栈2</title>
</head>
<body>
<!--
1. 依次输出什么?
gb: undefined
fb: 1
fb: 2
fb: 3
fe: 3
fe: 2
fe: 1
ge: 1
2. 整个过程中产生了几个执行上下文? 5
-->
<script type="text/javascript">
console.log('gb: ' + i);
var i = 1;
foo(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>
5、练习
<!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 a;
console.log(typeof a); // 'function'
/*
测试题2:
*/
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); // 报错
</script>
</body>
</html>
6、小结
3、作用域与作用域链
1、作用域
<!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 = 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 100
}
bar(100);
//bar(200);
}
fn(10);
</script>
</body>
</html>
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 = 20;
function fn(x) {
var a = 100,c = 300;
console.log('fn()', a, b, c, x);
function bar(x) {
var a = 1000,d = 400;
console.log('bar()', a, b, c, d, x);
}
bar(100);
bar(200);
}
fn(10);
</script>
</body>
</html>
3、作用域链
<!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 = 1;
function fn1() {
var b = 2;
function fn2() {
var c = 3;
console.log(c); //3
console.log(b); //2
console.log(a); //1
console.log(d); //报错
}
fn2();
}
fn1();
</script>
</body>
</html>
4、练习
<!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);
</script>
</body>
</html>
<!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>
5、小结
4、闭包
1、引入----循环遍历加监听
<!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 = i;
btn.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>
2、闭包理解
<!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 = 2;
var b = 'abc';
function fn2() {
//执行函数定义就会产生闭包(不用调用内部函数)
console.log(a);
}
// fn2();
}
fn1();
function fun1() {
var a = 3;
var fun2 = function() {
console.log(a);
}
}
fun1();
</script>
</body>
</html>
3、常见的闭包
<!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 = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f(); // 3
f(); // 4
// 2. 将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
setTimeout(function() {
alert(msg);
}, time);
}
showDelay('atguigu', 2000);
</script>
</body>
</html>
4、闭包的作用
<!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 = 2;
function fn2() {
a++;
console.log(a);
// return a
}
function fn3() {
a--;
console.log(a);
}
return fn3;
}
var f = fn1();
f(); // 1
f(); // 0
</script>
</body>
</html>
5、闭包的生命周期
<!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 = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f(); // 3
f(); // 4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>
</body>
</html>
6、闭包应用-自定义JS模块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="./JS/myModule.js"></script>
<script type="text/javascript">
var module = myModule();
module.doSomething();
module.doOtherthing();
</script>
</body>
</html>
myModule.js
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
}
}
7、闭包应用-自定义JS模块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="./JS/myModule2.js"></script>
<script type="text/javascript">
myModule2.doSomething();
myModule2.doOtherthing();
</script>
</body>
</html>
myModule2.js
(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
}
})()
8、闭包的缺点及解决
<!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>
9、内存溢出与内存泄漏
<!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 = 4;
function fn2() {
console.log(++a);
}
return fn2;
}
var f = fn1();
f();
// f = null;
</script>
</body>
</html>
10、练习题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
//代码片段二
var name2 = "The Window";
var object2 = {
name2: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name2;
};
}
};
alert(object2.getNameFunc()()); // my object
</script>
</body>
</html>
11、练习题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,0
var b = fun(0).fun(1).fun(2).fun(3); //undefined,0,1,2
var c = fun(0).fun(1);
c.fun(2);
c.fun(3); //undefined,0,1,1
</script>
</body>
</html>
三、面向对象高级
1、对象创建模式
1、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();
p = {
}; //此时内部数据是不确定的
// 再动态添加属性/方法
p.name = 'Tom';
p.age = 12;
p.setName = function(name) {
this.name = name;
}
//测试
console.log(p.name, p.age);
p.setName('Bob');
console.log(p.name, p.age);
</script>
</body>
</html>
2、对象字面量
<!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>
3、工厂模式
<!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>
4、自定义构造函数模式
<!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 = name;
this.age = age;
this.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 = name;
this.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>
5、构造函数+原型的组合模式
<!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 = name;
this.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>
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.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 = Sub;
Sub.prototype.showSubProp = function() {
console.log(this.subProp);
}
var sub = new Sub();
sub.showSupperProp();
// sub.toString();
sub.showSubProp();
console.log(sub);// Sub
</script>
</body>
</html>
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.name = name
this.age = age*/
this.price = price;
}
var s = new Student('Tom', 20, 14000);
console.log(s.name, s.age, s.price);
</script>
</body>
</html>
3、组合继承
<!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 = 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;//修正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>
四、线程机制与事件机制
1、进程与线程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01_进程与线程</title>
</head>
<body>
<!--
1. 进程:程序的一次执行, 它占有一片独有的内存空间
2. 线程: CPU的基本调度单位, 是程序执行的一个完整流程
3. 进程与线程
* 一个进程中一般至少有一个运行的线程: 主线程
* 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
* 一个进程内的数据可以供其中的多个线程直接共享
* 多个进程之间的数据是不能直接共享的
4. 浏览器运行是单进程还是多进程?
* 有的是单进程
* firefox
* 老版IE
* 有的是多进程
* chrome
* 新版IE
5. 如何查看浏览器是否是多进程运行的呢?
* 任务管理器==>进程
6. 浏览器运行是单线程还是多线程?
* 都是多线程运行的
-->
</body>
</html>
2、浏览器内核
<!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 + webkit
3. 内核由很多模块组成
* html,css文档解析模块 : 负责页面文本的解析
* dom/css模块 : 负责dom/css在内存中的相关处理
* 布局和渲染模块 : 负责页面的布局和效果的绘制
* 布局和渲染模块 : 负责页面的布局和效果的绘制
* 定时器模块 : 负责定时器的管理
* 网络请求模块 : 负责服务器请求(常规/Ajax)
* 事件响应模块 : 负责事件的管理
-->
</body>
</html>
3、定时器相关问题
<!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>
4、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>
5、事件循环模型
<!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>
6、H5 Web Workers(多线程)
注:这是H5新特性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>06_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" 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.value;
var result = fibonacci(number);
alert(result);
}
</script>
</body>
</html>
7、H5 Web Workers(多线程)
worker.js文件
function fibonacci(n) {
return n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2); //递归调用
}
console.log(this);
this.onmessage = function(event) {
var number = event.data;
console.log('分线程接收到主线程发送的数据: ' + number);
//计算
var result = fibonacci(number);
postMessage(result);
console.log('分线程向主线程返回数据: ' + result);
// alert(result) alert是window的方法, 在分线程不能调用
// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>06_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" 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('./js/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>
注意:
创建在分线程执行的js文件,写法模板
var onmessage =function (event){
//不能用函数声明
console.log('onMessage()22');
var upper = event.data.toUpperCase();//通过event.data获得发送来的数据
postMessage( upper );//将获取到的数据发送会主线程
}
在主线程中的js中发消息并设置回调
//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");
//接收worker传过来的数据函数
worker.onmessage = function (event) {
console.log(event.data);
};
//向worker发送数据
worker.postMessage("hello world");
8、H5 Web Workers缺点
- 慢
- 不能跨域加载JS
- worker内代码不能访问DOM(更新UI)
- 不是每个浏览器都支持这个新特性
9、小结
本篇博客是自己学习笔记的整理,仅供学习交流,切勿用于商业用途,如有侵权,请联系博主删除,博主QQ:1105810790