1. JavaScript的数据范例
1.1 JavaScript有几种范例的值
基础范例(值范例)
- 字符串(String)
- 数字(Number)
- 布尔(Boolean)
- 对空(Null)
- 未定义(Undefined)
- 举世无双的值(Symbol)
援用范例
- 对象(Object)
- 数组(Array)
- 函数(Function)
- …
1.2 基础数据范例
1.2.1 值是不可变的
var name = 'jie';
name.toUpperCase();
console.log(name); //jie
1.2.2 寄存在栈区
基础数据范例直接存储在栈(stack)中的简朴数据段
为何放入栈中存储
- 占有空间小
- 大小牢固
- 属于被频仍运用数据
1.2.3 值的比较
var a = 1;
var b = true;
console.log(a == b) //true
console.log(a === b); //false
- == : 只举行值的比较,会举行数据范例的转换。
- === : 不仅举行值得比较,还要举行数据范例的比较。
1.3 援用数据范例
1.3.1 值是可变的
var a = {
age: 20
}
var b = a;
b.age = 21;
console.log(a.age) //21
console.log(b.age) //21
1.3.2 同时保留在栈内存和堆内存
- 援用数据范例存储在堆(heap)中的对象,占有空间大、大小不牢固,假如存储在栈中,将会影响递次运转的机能;
- 援用数据范例在栈中存储了指针,该指针指向堆中该实体的肇端地点
- 当诠释器寻觅援用值时,会起首检索其在栈中的地点,取得地点后从堆中取得实体。
1.3.3比较是援用的比较
var a = {
age: 20
}
var b = a;
b.age = 21;
console.log(a.age) //21
console.log(b.age) //21
变量a初始化时,a指针指向对象{age:20}的地点,a赋值给b后,b又指向该对象{age:20}的地点,这两个变量指向了一致个对象。因而,转变个中任何一个变量,都邑互相影响。
假如作废某一个变量关于原对象的援用,不会影响到另一个变量
var a = {
age: 20
}
var b = a;
a = 1;
console.log(a) //1
console.log(b) //{age:20}
a和b指向一致个对象,然后a的值变成1,这时刻不会对b发生影响,b照样指向本来的谁人对象。
2.JavaScript检测
2.1 typeof
- typeof返回一个示意数据范例的字符串,返回结果包含:number、boolean、string、symbol、object、undefined、function等7种数据范例,
- 但不能推断null、array
typeof Symbol(); // symbol 有用
typeof ''; // string 有用
typeof 1; // number 有用
typeof true; //boolean 有用
typeof undefined; //undefined 有用
typeof new Function(); // function 有用
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
2.2 instanceof
- instanceof 是用来推断A是不是为B的实例,表达式为:A instanceof B,假如A是B的实例,则返回true,不然返回false。
- instanceof 运算符用来测试一个对象在其原型链中是不是存在一个组织函数的 prototype 属性。
- 不能检测null 和 undefined
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true
2.3 constructor
null 和 undefined 是无效的对象,因而是不会有 constructor 存在的,这两种范例的数据须要经由历程其他体式格局来推断
2.4Object.prototype.toString.call()
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局对象global的援用
2.5 参考
https://github.com/ljianshu/B…
3. 函数(要领)
3.1 天生函数的三种体式格局
3.1.1 函数声明
function fn(){
console.log('aa')
}
fn()
3.1.2 函数表达式
var fn = function() {
console.log('aa')
}
fn()
3.1.3 马上实行函数
var i = 'aa';
(function(i) {
console.log(i) //aa
})(i)
function fn(i) {
(function() {
console.log(i) //aa
})()
}
fn('aa')
function fn() {
(function(i) {
console.log(i) // undefined
})()
}
fn('aa')
function fn() {
(function() {
console.log(i)
})(i)
}
fn('aa')
3.2 函数的作用域
函数的作用域分为全局作用
3.2.1先声明变量,再挪用要领
var a = 'aa'
fn()
function fn() {
var b = 'bb'
c = 'cc'
console.log(a) //aa
console.log(b) //bb
console.log(c) //cc
}
console.log(a) //a
console.log(c) //cc
console.log(b) //b is not defined
3.2.先挪用要领,再声明变量
fn()
var a = 'aa'
function fn() {
var b = 'bb'
c = 'cc'
console.log(a) //aa
console.log(b) //bb
console.log(c) //cc
}
console.log(a) //undefined
console.log(b) //b is not defined
console.log(c) //cc
3.3 变量生命周期
JavaScript 变量生命周期在它声明时初始化。
- 局部变量在函数实行终了后烧毁。
- 全局变量在页面封闭后烧毁。
4. 原型到原型链
为何会存在
因为js要完成继承,js没有像别的言语有继承这个东西(es6中的class本质上也是基于原型和原型链),
4.1名词诠释
- constructor 组织函数
- prototype 原型(显式原型),只需函数才有 prototype 属性
__proto__
原型链(隐式原型),每一个JavaScript对象(除了 null )都具有的一个属性,函数也是对象,所以函数也有__proto__4.2 组织函数建立对象
4.2.1最简朴的组织函数
- 函数名字为大写
- 与new合营
- Person 就是一个组织函数,我们运用 new 建立了一个实例对象 person
function Person() {
}
var person = new Person();
person.name = 'jie'
console.log(person.name)
4.2.2 prototype(原型)
那什么是原型呢
每一个JavaScript对象(null除外)在建立的时刻就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都邑从原型”继承”属性
- 每一个函数都有一个 prototype 属性
- 函数的 prototype 属性指向了一个对象,这个对象恰是挪用该组织函数而建立的实例的原型,也就是这个例子中的 person1 和 person2 的原型
function Person() {
}
Person.prototype.name = 'jie'
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) //jie
console.log(person2.name) //jie
用一张图示意组织函数和实例原型之间
4.2.3 proto
- 每一个JavaScript对象(除了 null )都具有的一个属性,叫__proto__,
- 这个属性会指向该对象的原型
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype) //true
用一张图示意实例和实例原型之间的__proto__
4.2.4 constructor
每一个原型都有一个 constructor 属性指向关联的组织函数
function Person() {
}
console.log(Person === Person.prototype.constructor) //true
4.3 实例与原型
- 当读取实例的属性时,假如找不到,就会查找与对象关联的原型中的属性
- 假如还查不到,就去找原型的原型,一向找到最顶层为止
function Person() {
}
Person.prototype.name = '原型上的名字';
var person1 = new Person();
var person2 = new Person();
person1.name = '实例名字1';
person2.name = '实例名字2';
console.log('person1.name:' + person1.name) //实例名字1
console.log('person2.name:' + person2.name) //实例名字2
delete person1.name;
console.log('person1.name:' + person1.name) //原型上的名字
console.log('person2.name:' + person2.name) //实例名字1
4.4 原型的原型
原型也是一个对象,既然是对象,我们就能够用最原始的体式格局建立它
var obj = new Object();
obj.name = 'jie'
console.log(obj.name)
4.5 原型链
function Person() {
}
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype) //true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__ === null) //true
图中由互相关联的原型构成的链状构造就是原型链,也就是蓝色的这条线。
4.6 别的
4.6.1 person.constructor
function Person() {
}
var person = new Person();
console.log(person.constructor === Person); // true
当猎取 person.constructor 时,实在 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,恰好原型中有该属性,所以:
person.constructor === Person.prototype.constructor
4.6.2 真的是继承吗
继承意味着复制操纵,然则 JavaScript 默许并不会复制对象的属性,相反,JavaScript 只是在两个对象之间建立一个关联,如许,一个对象就能够经由历程托付接见另一个对象的属性和函数,所以与其叫继承,托付的说法反而更正确些
4.7 参考
https://github.com/mqyqingfen…
5.词法作用域
JavaScript 采纳词法作用域(lexical scoping),也就是静态作用域
var value = 1;
function fn1() {
console.log(value)
}
function fn2() {
var value = 2;
fn1()
}
fn2()
依据函数栈先进后出的递次实行,先实行完fn1(),再实行完fn2()
实行 fn1 函数,先从 fn1 函数内部查找是不是有局部变量 value,假如没有,就依据誊写的位置(),查找上面一层的代码,也就是 value 即是 1,所以结果会打印 1
6.实行上下文
6.1 声明(建立) JavaScript 变量
运用 var 关键词来声明变量
var carname;
变量声明以后,该变量是空的(它没有值)。
如需向变量赋值,请运用等号:
carname="Volvo";
不过,您也能够在声明变量时对其赋值:
var carname="Volvo";
6.2 变量声明的各种状况
6.2.1 打印未声明和未赋值
console.log(a)
6.2.2 先打印,再声明和未赋值
console.log(a) //undefined
var a;
6.2.3 先打印,再声明和未赋值
console.log(a) //undefined
var a = 10;
6.2.4 声明和未赋值,再打印,
var a = 10;
console.log(a)
6.3 函数声明的各种状况
6.3.1 函数声明
console.log(f1)
function f1() {
}
6.3.2 函数表达式
console.log(f2)
var f2 = function() {
}
6.4 this
console.log(this)
6.5 什么是实行上下文
实行上下文也叫做实行上下文环境
给实行上下文环境下一个浅显的定义:在实行代码之前,把将要用到的一切的变量都事前拿出来,有的直接赋值了,有的先用undefined占个空
- 变量、函数表达式——变量声明,默许赋值为undefined;
- this——赋值;
- 函数声明——赋值;
这三种数据的预备状况我们称之为“实行上下文”或许“实行上下文环境”
6.6 函数中的实行上下文
以下代码展现了在函数体的语句实行之前,arguments变量和函数的参数都已被赋值。从这里能够看出,函数每被挪用一次,都邑发生一个新的实行上下文环境。因为差别的挪用能够就会有差别的参数
function fn(x) {
console.log(arguments)
console.log(x)
}
fn(10)
fn(20)
6.6 总结
全局代码的上下文环境数据内容为
平常变量(包含函数表达式),如: var a = 10; | 声明(默许赋值为undefined) |
---|---|
函数声明,如: function fn() { } | 赋值 |
this | 赋值 |
假如代码段是函数体,那末在此基础上须要附加
参数 | 赋值 |
---|---|
arguments | 赋值 |
自在变量的取值作用域 | 赋值 |
6.7 参考
http://www.cnblogs.com/wangfu…
7.实行上下文栈
7.1 什么是实行上下文栈
实行全局代码时,会发生一个实行上下文环境,每次挪用函数都又会发生实行上下文环境。当函数挪用完成时,这个上下文环境以及个中的数据都邑被消弭,再从新回到全局上下文环境。处于运动状况的实行上下文环境只需一个
这是一个压栈出栈的历程——实行上下文栈
7.2 上下文栈的压栈、出栈历程
- 在实行代码之前,起首将建立全局上下文环境
- 然后是代码实行。代码实行到第12行之前,上下文环境中的变量都在实行历程当中被赋值。
- 实行到第13行,挪用bar函数。
跳转到bar函数内部,实行函数体语句之前,会建立一个新的实行上下文环境。 - 并将这个实行上下文环境压栈,设置为运动状况
- 实行到第5行,又挪用了fn函数。进入fn函数,在实行函数体语句之前,会建立fn函数的实行上下文环境,并压栈,设置为运动状况。
- 待第5行实行终了,即fn函数实行终了后,此次挪用fn所天生的上下文环境出栈,而且被烧毁(已用完了,就要实时烧毁,开释内存)。
- 同理,待第13行实行终了,即bar函数实行终了后,挪用bar函数所天生的上下文环境出栈,而且被烧毁(已用完了,就要实时烧毁,开释内存)。
8. this
8.1 推断剖析当前this
- 关于直接挪用 foo 来讲,不论 foo 函数被放在了什么处所,this 一定是 window
- 关于 obj.foo() 来讲,我们只须要记着,谁挪用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
- 在组织函数形式中,类中(函数体中)涌现的this.xxx=xxx中的this是当前类的一个实例
- call、apply和bind:this 是第一个参数
- 箭头函数this指向:箭头函数没有自身的this,看其外层的是不是有函数,假如有,外层函数的this就是内部箭头函数的this,假如没有,则this是window。
8.2 this的5种状况
8.2.1 直接挪用foo()
function foo() {
console.log(this)
}
var a = 1;
foo()
8.2.2 obj.foo()
function fn() {
console.log(this)
}
var obj = {
fn: fn
}
obj.fn();
8.2.3 组织函数
function CreateJsPerson(name, age) {
//this是当前类的一个实例p1
this.name = name; //=>p1.name=name
this.age = age; //=>p1.age=age
console.log(this)
}
var p1 = new CreateJsPerson("尹华芝", 48);
8.2.4 call、apply和bind
function add(c, d) {
console.log(this)
return this.a + this.b + c + d;
}
var o = {
a: 1,
b: 3
};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
8.2.5 箭头函数
<button id="btn1">箭头函数this</button>
<script>
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function() {
btn1.onclick = () => {
console.log(this); //obj
};
}
};
obj.getName();
</script>
8.3 参考
https://github.com/ljianshu/B…
9. 作用域和上下文环境
作用域在函数定义时就已肯定了。而不是在函数挪用时肯定
9.1 递次实行时的,上下文环境
依据递次实行的递次,一步一步把各个上下文环境
- 在加载递次时,已肯定了全局上下文环境,并跟着递次的实行而对变量就行赋值
- 递次实行到第27行,挪用fn(10),此时天生此次挪用fn函数时的上下文环境,压栈,并将此上下文环境设置为运动状况。
- 行到第23行时,挪用bar(100),天生此次挪用的上下文环境,压栈,并设置为运动状况
- 实行完第23行,bar(100)挪用完成。则bar(100)上下文环境被烧毁。接着实行第24行,挪用bar(200),则又天生bar(200)的上下文环境,压栈,设置为运动状况。
- 实行完第24行,则bar(200)挪用完毕,其上下文环境被烧毁。此时会回到fn(10)上下文环境,变成运动状况。
- 实行完第27行代码,fn(10)实行完成以后,fn(10)上下文环境被烧毁,全局上下文环境又回到运动状况。
9.2 参考
http://www.cnblogs.com/wangfu…
10. 自在变量到作用域链
10.1 自在变量
在A作用域中运用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),关于A作用域来讲,x就是一个自在变量
var x = 10;
function fn() {
var b = 20;
console.log(x + b) //这里的x在这里就是一个自在变量
}
而取x的值时,就须要到另一个作用域中取。到哪一个作用域中取呢?
要到建立这个函数的谁人作用域中取值——是“建立”,而不是“挪用”,牢记牢记
var x = 10;
function fn() {
console.log(x) //10
}
function show(f) {
var x = 20;
(function() {
f()
})()
}
show(fn)
10.2 作用域链
假如跨了一步,还没找到呢?——接着跨!——一向跨到全局作用域为止。如果在全局作用域中都没有找到,那就是真的没有了。
这个一步一步“跨”的线路,我们称之为——作用域链
取自在变量时的这个“作用域链”历程:(假定a是自在量)
- 如今当前作用域查找a,假如有则猎取并完毕。假如没有则继承;
- 假如当前作用域是全局作用域,则证实a未定义,完毕;不然继承;
- 不是全局作用域,那就是函数作用域)将建立该函数的作用域作为当前作用域;
- 跳转到第一步。
实例
第13行,fn()返回的是bar函数,赋值给x。实行x(),即实行bar函数代码。取b的值时,直接在fn作用域掏出。取a的值时,试图在fn作用域取,然则取不到,只能转向建立fn的谁人作用域中去查找,结果找到了。
11. 闭包
11.1 什么是闭包
闭包是指能够接见另一个函数作用域变量的函数,平常是定义在外层函数中的内层函数。
11.2 为何须要闭包呢
局部变量没法同享和久长的保留,而全局变量能够形成变量污染,所以我们愿望有一种机制既能够久长的保留变量又不会形成全局污染。
11.3 特性
- 占用更多内存
- 不容易被开释
11.4作用
- 庇护
- 保留
11.5 什么时候运用
既想反复运用,又想防止全局污染
11.6 怎样运用
- 定义外层函数,封装被庇护的局部变量。
- 定义内层函数,实行对外部函数变量的操纵。
- 外层函数返回内层函数的对象,而且外层函数被挪用,结果保留在一个全局的变量中。
11.7 实例
- 代码实行前天生全局上下文环境,并在实行时对个中的变量举行赋值。此时全局上下文环境是运动状况
- 实行第17行代码时,挪用fn(),发生fn()实行上下文环境,压栈,并设置为运动状况。
- 实行完第17行,fn()挪用完成
按理说应当烧毁掉fn()的实行上下文环境,然则这里不能这么做。注重,重点来了:因为实行fn()时,返回的是一个函数。函数的特别之处在于能够建立一个自力的作用域。而正偶合的是,返回的这个函数体中,另有一个自在变量max要援用fn作用域下的fn()上下文环境中的max。因而,这个max不能被烧毁,烧毁了以后bar函数中的max就找不到值了。
因而,这里的fn()上下文环境不能被烧毁,还依旧存在与实行上下文栈中。
- 实行到第18行时
全局上下文环境将变成运动状况,然则fn()上下文环境依旧会在实行上下文栈中。别的,实行完第18行,全局上下文环境中的max被赋值为100。以下图: - 实行到第20行,实行f1(15),即实行bar(15),建立bar(15)上下文环境,并将其设置为运动状况
建立bar函数是在实行fn()时建立的。fn()早就实行完毕了,然则fn()实行上下文环境还存在与栈中,因而bar(15)时,max能够查找到。假如fn()上下文环境烧毁了,那末max就找不到了
- 实行完20行就是上下文环境的烧毁历程
11.8 闭包口试剖析实例
- 按原理,挪用fn1()以后,就会烧毁fn1()
- 然则此时的result为 function(){console.log(n)},(result为fn1()的返回值)
- 假如要挪用result(),然则result内里的n是自在变量(函数的特别之处在于能够建立一个自力的作用域,result函数体内没有定义n,要到他的上层作用域找),
- 在result的上层作用域fn1()里找到了n
- 因而result依靠fn1()中的n,所以fn1()在挪用后,并不能烧毁,fn1()中的n一向存在
function fn1() {
var n = 0;
return function() {
console.log(n)
}
}
var result = fn1();
result()
11.9 参考
http://www.cnblogs.com/wangfu…
https://zhuanlan.zhihu.com/p/…
http://www.ruanyifeng.com/blo…
12. new运算符的实行历程
12.1 步骤
- 用new Object() 的体式格局新建了一个对象 obj
- 掏出第一个参数,就是我们要传入的组织函数。(另外因为 shift 会修正原数组,所以 arguments 会被去除第一个参数)
- 将 obj 的原型指向组织函数,如许 obj 就能够接见到组织函数原型中的属性
- 运用 apply,转变组织函数 this 的指向到新建的对象,如许 obj 就能够接见到组织函数中的属性
- 返回 obj,(推断返回的值是不是是一个对象,假如是一个对象,我们就返回这个对象,假如没有,我们该返回什么就返回什么)
12.2 实例
function Person(name, age) {
this.name = name;
this.age = age;
this.call = function() {
alert(this.name + this.age)
}
}
var p1 = new Person('jie', 12)
p1.call()
function myNew3() {
let obj = new Object(); //1
let Constructor = [].shift.call(arguments); //2
obj.__proto__ = Constructor.prototype; //3
let result = Constructor.apply(obj, arguments); //4
if (result instanceof Object) { //5
return result
} else {
return obj;
}
}
var p4 = myNew3(Person, 'wei', 14)
p4.call()
12.3 参考
https://github.com/mqyqingfen…
13.call
13.1 定义
- 浅显的理解为借用(一个对象没有这个要领,然则别的对象有,不想反复代码,所以借来用一下)
- call() 要领在运用一个指定的 this 值和若干个指定的参数值的前提下挪用某个函数或要领。
13.2 实例
var obj = {
value: '1'
}
function fn(name, age) {
this.name = name;
this.age = age;
this.say = function() {
alert(this.name + this.age)
}
}
fn.call(obj, 'jie', 10)
obj.say()
13.3 call的模仿完成
- 将函数设为对象的属性
- 实行该函数
- 删除该函数
Function.prototype.call2 = function(context) {
var context = context || window;
context.fn = this;
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(`arguments[${i}]`)
}
var result = eval(`context.fn(${args})`)
delete context.fn;
return result;
}
fn.call2(obj, 'biao', 20)
obj.say()
13.4 参考
https://www.cnblogs.com/moqiu…
https://github.com/mqyqingfen…
14. apply
apply与call范例,只是传参不一样
14.1 apply的模仿完成
var obj = {
value: '1'
}
function fn(name, age) {
this.name = name;
this.age = age;
this.say = function() {
alert(this.name + this.age)
}
}
Function.prototype.apply2 = function(context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn()
} else {
var args = [];
for (var i = 0; i < arr.length; i++) {
args.push(`arr[${i}]`)
}
result = eval(`context.fn(${args})`)
}
delete context.fn;
return result;
}
fn.apply(obj, ['jie', 10])
obj.say()
fn.apply2(obj, ['biao', 20])
obj.say()
15. bind
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
// 返回了一个函数
var bindFoo = bar.bind(foo);
bindFoo(); // 1
16. arguments
是一个对应于通报给函数的参数的类数组对象。
function foo(name, age, sex) {
console.log(arguments)
}
foo('name', 'age', 'sex')
function fn(...arguments) {
console.log(arguments)
}
fn(1, 2, 3)
17 继承
17.1组合继承
function Person() {
this.name = 'jie'
this.color = ['red', 'blue']
}
Person.prototype.say = function() {
console.log(this.name)
}
function Student(id) {
Person.call(this)
this.id = id
}
Student.prototype = new Person()
Student.prototype.constructor = Student
var s1 = new Student('1')
console.log(s1.name)
console.log(s1.color)
s1.say()
s1.color.push('yellow')
console.log(s1.color)
var s2 = new Student('2')
console.log(s2.name)
console.log(s2.color)
s2.say()
s2.color.push('white')
console.log(s2.color)
https://www.cnblogs.com/sarah…
18 异步
18.1 单线程
18.1.1 什么是单线程
Javascript言语的实行环境是”单线程”(single thread)
所谓”单线程”,就是指一次只能完成一件使命。假如有多个使命,就必须列队,前面一个使命完成,再实行背面一个使命,以此类推。
单线程的长处
这类形式的优点是完成起来比较简朴,实行环境相对纯真
单线程的瑕玷
害处是只需有一个使命耗时很长,背面的使命都必须列队等着,会迁延全部递次的实行。罕见的浏览器无相应(假死),每每就是因为某一段Javascript代码长时候运转(比方死轮回),致使全部页面卡在这个处所,其他使命没法实行。
处置惩罚单线程的瑕玷
为了处置惩罚这个题目,Javascript言语将使命的实行形式分红两种:同步(Synchronous)和异步(Asynchronous)。
同步形式
后一个使命守候前一个使命完毕,然后再实行,递次的实行递次与使命的分列递次是一致的、同步的
异步形式
每一个使命有一个或多个回调函数(callback),前一个使命完毕后,不是实行后一个使命,而是实行回调函数,后一个使命则是不等前一个使命完毕就实行,所以递次的实行递次与使命的分列递次是不一致的、异步的。
异步形式的情形
http要求
$.ajax({
url: '',
success: function(data) {
console.log(data)
},
error: function(error) {
console.log(error)
}
})
18 异步
18.1 单线程
18.1.1 什么是单线程
Javascript言语的实行环境是”单线程”(single thread)
所谓”单线程”,就是指一次只能完成一件使命。假如有多个使命,就必须列队,前面一个使命完成,再实行背面一个使命,以此类推。
单线程的长处
这类形式的优点是完成起来比较简朴,实行环境相对纯真
单线程的瑕玷
害处是只需有一个使命耗时很长,背面的使命都必须列队等着,会迁延全部递次的实行。罕见的浏览器无相应(假死),每每就是因为某一段Javascript代码长时候运转(比方死轮回),致使全部页面卡在这个处所,其他使命没法实行。
处置惩罚单线程的瑕玷
为了处置惩罚这个题目,Javascript言语将使命的实行形式分红两种:同步(Synchronous)和异步(Asynchronous)。
同步形式
后一个使命守候前一个使命完毕,然后再实行,递次的实行递次与使命的分列递次是一致的、同步的
异步形式
每一个使命有一个或多个回调函数(callback),前一个使命完毕后,不是实行后一个使命,而是实行回调函数,后一个使命则是不等前一个使命完毕就实行,所以递次的实行递次与使命的分列递次是不一致的、异步的。
18.1.2罕见的异步操纵
- 收集要求,如ajax http.get
- IO 操纵,如readFile readdir
- 定时函数,如setTimeout setInterval
18.2 回调函数(Callback)
// 1秒后打印出aa
function fn1(callback) {
setTimeout(function() {
callback();
}, 1000)
}
fn1(fn2)
function fn2() {
console.log('aa')
}
18.3 回调地狱
ajax(url, () => {
// 处置惩罚逻辑
ajax(url1, () => {
// 处置惩罚逻辑
ajax(url2, () => {
// 处置惩罚逻辑
})
})
})
18.3.1 callback封装
function ajaxFn(url, callback) {
$.ajax({
method: 'get',
url: url,
success: function(data) {
callback(data)
},
error: function(error) {
console.log(error)
}
})
}
ajaxFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312', (res) => {
console.log(res)
console.log('第一个要求完成')
ajaxFn('https://cnodejs.org/api/v1/topics', (ress) => {
console.log(ress)
console.log('第二个要求完成')
})
})
18.3.2 promise封装
function promiseFn(url) {
return new Promise((resolve, reject) => {
$.ajax({
method: 'get',
url: url,
success: function(data) {
resolve(data)
},
error: function(error) {
reject(error)
}
})
})
}
promiseFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312')
.then(res => {
console.log(res)
console.log('第一个要求完成')
return promiseFn('https://cnodejs.org/api/v1/topics')
})
.then(res => {
console.log(res)
console.log('第二个要求完成')
})
.catch(err => {
console.log(err)
})
18.3.3 async/await封装
async function asyncFn(url) {
return await new Promise((resolve, reject) => {
$.ajax({
method: 'get',
url: url,
success: function(response) {
resolve(response);
},
error: function(error) {
reject(error);
}
})
})
}
async function start() {
var result1 = await asyncFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312');
var result2 = await asyncFn('https://cnodejs.org/api/v1/topics');
console.log(result1)
console.log('第一个要求完成')
console.log(result2)
console.log('第二个要求完成')
}
start()
18.4 壅塞、非壅塞、同步、异步
18.4.1 生涯中实例剖析(热水壶烧水)
在良久之前,科技还没有这么兴旺的时刻,假如我们要烧水,须要把水壶放到火炉上,我们经由历程视察水壶内的水的沸腾水平来推断水有无烧开
跟着科技的生长,如今市面上的水壶都有了提示功用,当我们把水壶插电以后,水壶水烧开以后会经由历程声响提示我们水开了。
18.4.2 同步、异步
关于烧水这件事儿来讲,传统水壶的烧水就是同步的,高科技水壶的烧水就是异步的
18.4.3 壅塞、非壅塞
当你把水放到水壶内里,按下开关后,你能够坐在水壶前面,别的事变什么都不做,一向等着水烧好。你还能够先去客堂看电视,等着水开就好了。
关于你来讲,坐在水壶前面等就是壅塞的,去客堂看电视等着水开就黑白壅塞的。
18.4.4 壅塞、非壅塞和同步、异步的区分
壅塞、非壅塞说的是挪用者(运用水壶的我),同步、异步说的是被挪用者(水壶)。
19. 深浅拷贝
19.1 js 数据范例提及
- 基础数据范例保留在栈内存,
- 援用范例保留在堆内存中
19.1.1 栈内存,堆内存
var a = 1;//定义了一个number范例
var obj1 = {//定义了一个object范例
name:'obj'
};
19.1.2 基础范例的复制
var a = 1;
var b = a;
console.log(a) //1
console.log(b) //1
b = 2;
console.log(a) //1
console.log(b) //2
赋值的时刻,在栈内存中从新拓荒内存,寄存变量b,所以在栈内存中离别寄存着变量a、b各自的值,修正时互不影响
19.1.3 援用范例的复制
var color1 = ['red', 'blue']
var color2 = color1;
console.log(color1) //["red", "blue"]
console.log(color2) //["red", "blue"]
color1.push('black');
color1.push('yellow');
console.log(color1) // ["red", "blue", "black", "yellow"]
console.log(color2) //["red", "blue", "black", "yellow"]
color1与color2指向堆内存中一致地点的一致对象,复制的只是援用地点
因而,关于援用范例的复制,简朴赋值无用,须要拷贝。拷贝存在两种范例:深拷贝与浅拷贝
19.2 浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象自身,新旧对象照样同享一致块内存
修正新对象会改到原对象
var person = {
p1: {
name: 'jie',
age: 18
},
p2: 'biao'
}
var person1 = person;
person1.p1.name = 'nine';
console.log(person) //nine
console.log(person1) //nine
19.3 深拷贝
深拷贝会别的制造一个如出一辙的对象,新对象跟原对象不同享内存,修正新对象不会改到原对象
修正新对象不会改到原对象
function depClone(obj) {
var result = JSON.parse(JSON.stringify(obj));
return result;
}
var person = {
p1: {
name: 'jie',
age: 18
},
p2: 'biao'
}
var person2 = depClone(person);
person2.p1.name = 'nine';
console.log(person) //jie
console.log(person2) //nine
19.4 参考
https://www.cnblogs.com/136as…
https://github.com/mqyqingfen…
20 DOM事宜机制
20.1 DOM事宜级别
20.2 DOM 0级事宜
20.2.1 最简朴的DOM 0级事宜
<button type="button" onclick="fn" id="btn">点我尝尝</button>
<script>
function fn() {
alert('Hello World');
}
</script>
20.2.2 onclick="fn"
要不要加()
没有()
button type="button" onclick="fn" id="btn">点我尝尝</button>
<script>
function fn() {
alert('Hello World');
}
console.log(document.getElementById('btn').onclick);
</script>
不弹出框(没有实行alert())
有()
button type="button" onclick="fn" id="btn">点我尝尝</button>
<script>
function fn() {
alert('Hello World');
}
console.log(document.getElementById('btn').onclick);
</script>
弹出框(实行alert())
20.3 DOM2级事宜
- 第一个参数是监听行动
- 第二个参数监听事宜
- 第三个参数false冒泡,true捕捉
<button type="button" id="btn">点我尝尝</button>
<script>
var btn = document.getElementById('btn');
function fn() {
alert('Hello World');
}
btn.addEventListener('click', fn, false);
// 解绑事宜,代码以下
// btn.removeEventListener('click', fn, false);
</script>
20.4 DOM3级事宜
- DOM3级事宜在DOM2级事宜的基础上添加了更多的事宜范例
- UI事宜,当用户与页面上的元素交互时触发,如:load、scroll
- 核心事宜,当元素取得或落空核心时触发,如:blur、focus
- 鼠标事宜,当用户经由历程鼠标在页面实行操纵时触发如:dbclick、mouseup
- 滚轮事宜,当运用鼠标滚轮或相似装备时触发,如:mousewheel
- 文本事宜,当在文档中输入文本时触发,如:textInput
- 键盘事宜,当用户经由历程键盘在页面上实行操纵时触发,如:keydown、keypress
- 合成事宜,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
- 更改事宜,当底层DOM构造发生变化时触发,如:DOMsubtreeModified
20.4 DOM事宜流
20.4.1 为何是有事宜流?
假如在一个button上注册了一个click事宜,又在别的父元素div上注册了一个click事宜,那末当我们点击button,是先触发父元素上的事宜,照样button上的事宜呢,这就须要一种商定去范例事宜的实行递次,就是事宜实行的流程。
浏览器在生长的历程当中涌现了两种差别的范例
- 9以下的IE浏览器运用的是事宜冒泡,先从详细的吸收元素,然后逐渐向上流传到不详细的元素。
- Netscapte采纳的是事宜捕捉,先由不详细的元素吸收事宜,最详细的节点末了才吸收到事宜。
- 而W3C制订的Web规范中,是同时采纳了两种计划,事宜捕捉和事宜冒泡都能够。
20.5 DOM事宜模子
20.5.1 什么是DOM事宜模子
DOM事宜模子分为捕捉和冒泡。一个事宜发生后,会在子元素和父元素之间流传(propagation)。这类流传分红三个阶段。
- 捕捉阶段:事宜从window对象自上而下向目的节点流传的阶段;
- 目的阶段:真正的目的节点正在处置惩罚事宜的阶段;
- 冒泡阶段:事宜从目的节点自下而上向window对象流传的阶段。
20.5.2 事宜捕捉
捕捉是从上到下,事宜先从window对象,然后再到document(对象),然后是html标签(经由历程document.documentElement猎取html标签),然后是body标签(经由历程document.body猎取body标签),然后依据平常的html构造一层一层往下传,末了抵达目的元素。我们只须要将addEventListener的第三个参数改成true就能够完成事宜捕捉
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
margin: 0;
}
div {
border: 1px solid #000;
}
#grandfather1 {
width: 200px;
height: 200px;
}
#parent1 {
width: 100px;
height: 100px;
margin: 0 auto;
}
#child1 {
width: 50px;
height: 50px;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="grandfather1">
爷爷
<div id="parent1">
父亲
<div id="child1">儿子</div>
</div>
</div>
</body>
<script>
var grandfather1 = document.getElementById('grandfather1'),
parent1 = document.getElementById('parent1'),
child1 = document.getElementById('child1');
grandfather1.addEventListener('click', function fn1() {
console.log('爷爷');
}, true)
parent1.addEventListener('click', function fn1() {
console.log('爸爸');
}, true)
child1.addEventListener('click', function fn1() {
console.log('儿子');
}, true)
</script>
</html>
点击id为child1的div标签时(儿子框),打印的结果是爷爷 => 爸爸 => 儿子,。
20.5.2 事宜冒泡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
margin: 0;
}
div {
border: 1px solid #000;
}
#grandfather1 {
width: 200px;
height: 200px;
}
#parent1 {
width: 100px;
height: 100px;
margin: 0 auto;
}
#child1 {
width: 50px;
height: 50px;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="grandfather1">
爷爷
<div id="parent1">
父亲
<div id="child1">儿子</div>
</div>
</div>
</body>
<script>
var grandfather1 = document.getElementById('grandfather1'),
parent1 = document.getElementById('parent1'),
child1 = document.getElementById('child1');
grandfather1.addEventListener('click', function fn1() {
console.log('爷爷');
}, false)
parent1.addEventListener('click', function fn1() {
console.log('爸爸');
}, false)
child1.addEventListener('click', function fn1() {
console.log('儿子');
}, false)
</script>
</html>
点击id为child1的div标签时(儿子框),打印的结果是儿子=>爸爸=>爷爷
20.6 事宜代办(事宜托付)
20.6.1 事宜代办寄义和为何要优化?
因为事宜会在冒泡阶段向上流传到父节点,因而能够把子节点的监听函数定义在父节点上,由父节点的监听函数一致处置惩罚多个子元素的事宜。这类要领叫做事宜的代办(delegation)。
举个例子,比方一个宿舍的同砚同时快递到了,一种要领就是他们都傻傻地一个个去领取,另有一种要领就是把这件事变托付给宿舍长,让一个人出去拿好一切快递,然后再依据收件人逐一分发给每一个宿舍同砚;
在这里,取快递就是一个事宜,每一个同砚指的是须要相应事宜的 DOM 元素,而出去一致领取快递的宿舍长就是代办的元素,所以真正绑定事宜的是这个元素,依据收件人分发快递的历程就是在事宜实行中,须要推断当前相应的事宜应当婚配到被代办元素中的哪一个或许哪几个。
那末应用事宜冒泡或捕捉的机制,我们能够对事宜绑定做一些优化。 在JS中,假如我们注册的事宜愈来愈多,页面的机能就愈来愈差,因为:
- 函数是对象,会占用内存,内存中的对象越多,浏览器机能越差
- 注册的事宜平常都邑指定DOM元素,事宜越多,致使DOM元素接见次数越多,会耽误页面交互停当时候。
- 删除子元素的时刻不必斟酌删除绑定事宜
20.6.2 长处
- 削减内存斲丧,进步机能
假如给每一个列表项逐一都绑定一个函数,那关于内存斲丧黑白常大的,效力上须要斲丧许多机能。借助事宜代办,我们只须要给父容器ul绑定要领即可,如许不论点击的是哪一个子女元素,都邑依据冒泡流传的通报机制,把容器的click行动触发,然后把对应的要领实行,依据事宜源,我们能够晓得点击的是谁,从而完成差别的事。 - 动态绑定事宜
在许多时刻,我们须要经由历程用户操纵动态的增删列表项元素,假如一开始给每一个子元素绑定事宜,那末在列表发生变化时,就须要从新给新增的元素绑定事宜,给行将删去的元素解绑事宜,假如用事宜代办就会省去许多如许贫苦。
20.6.3 代码实例
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
// 给父层元素绑定事宜
document.getElementById('list').addEventListener('click', function(e) {
// 兼容性处置惩罚
var event = e || window.event;
var target = event.target || event.srcElement;
// 推断是不是婚配目的元素
if (target.nodeName.toLocaleLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
</script>
这是通例的完成事宜托付的要领,然则这类要领有BUG,当监听的元素里存在子元素时,那末我们点击这个子元素事宜会失效,所以我们能够联络文章上一小节说到的冒泡事宜流传机制来处置惩罚这个bug。革新的事宜托付代码:
<ul id="list">
<li>1 <span>aaaaa</span></li>
<li>2 <span>aaaaa</span></li>
<li>3 <span>aaaaa</span></li>
<li>4</li>
</ul>
<script>
document.getElementById('list').addEventListener('click', function(e) {
// 兼容性处置惩罚
var event = e || window.event;
var target = event.target || event.srcElement;
// 推断是不是婚配目的元素
/* 从target(点击)元素向上找currentTarget(监听)元素,
找到了想托付的元素就触发事宜,没找到就返回null */
while (target.tagName !== 'LI') {
if (target.tagName === 'UL') {
target = null
break;
}
target = target.parentNode
}
if (target) {
// console.log('你点击了ul里的li')
console.log('the content is: ', target.innerHTML);
}
});
</script>
20.7 Event对象罕见的要领和属性
20.7.1 event. preventDefault()
preventDefault阻挠默许行动
假如挪用这个要领,默许事宜行动将不再触发。什么是默许事宜呢?比方表单一点击提交按钮(submit)革新页面、a标签默许页面跳转或是锚点定位等。
a标签默许页面跳转
<a id="test" href="http://www.google.com">链接</a>
<script>
test.onclick = function(e){
e = e || window.event;
e.preventDefault();
}
</script>
输入框最多只能输入六个字符
<input type="text" id='tempInp'>
<script>
tempInp.onkeydown = function(ev) {
ev = ev || window.event;
let val = this.value.trim() //trim去除字符串首位空格(不兼容)
// this.value=this.value.replace(/^ +| +$/g,'') 兼容写法
let len = val.length
if (len >= 6) {
this.value = val.substr(0, 6);
//阻挠默许行动去除特别按键(DELETE\BACK-SPACE\方向键...)
let code = ev.which || ev.keyCode;
if (!/^(46|8|37|38|39|40)$/.test(code)) {
ev.preventDefault()
}
}
}
</script>
20.7.2 event.stopPropagation()
stopPropagation住手冒泡
event.stopPropagation() 要领阻挠事宜冒泡到父元素,阻挠任何父事宜处置惩罚递次被实行
<body>
<div id="grandfather1">
爷爷
<div id="parent1">
父亲
<div id="child1">儿子</div>
</div>
</div>
</body>
<script>
var grandfather1 = document.getElementById('grandfather1'),
parent1 = document.getElementById('parent1'),
child1 = document.getElementById('child1');
grandfather1.addEventListener('click', function fn1() {
console.log('爷爷');
}, false)
parent1.addEventListener('click', function fn1() {
console.log('爸爸');
event.stopPropagation()
}, false)
child1.addEventListener('click', function fn1() {
console.log('儿子');
}, false)
点击儿子的时刻,打印儿子,爸爸
点击爸爸的时刻,打印爸爸
20.7.3 stopImmediatePropagation
既能阻挠事宜向父元素冒泡,也能阻挠元素同事宜范例的别的监听器被触发。而 stopPropagation 只能完成前者的结果
<button id="btn">点我尝尝</button>
<script>
const btn = document.querySelector('#btn');
btn.addEventListener('click', event => {
console.log('btn click 1');
event.stopImmediatePropagation();
});
btn.addEventListener('click', event => {
console.log('btn click 2');
});
document.body.addEventListener('click', () => {
console.log('body click');
});
</script>
只打印console.log('btn click 1');
<body>
<div id="grandfather1">
爷爷
<div id="parent1">
父亲
<div id="child1">儿子</div>
</div>
</div>
</body>
<script>
var grandfather1 = document.getElementById('grandfather1'),
parent1 = document.getElementById('parent1'),
child1 = document.getElementById('child1');
grandfather1.addEventListener('click', function fn1() {
console.log('爷爷');
}, false)
parent1.addEventListener('click', function fn1() {
console.log('爸爸');
event.stopImmediatePropagation();
}, false)
child1.addEventListener('click', function fn1() {
console.log('儿子');
}, false)
</script>
点击儿子的时刻,打印儿子,爸爸
点击爸爸的时刻,打印爸爸
20.8 参考
https://juejin.im/post/5c71e8…
https://github.com/ljianshu/B…
21 随机天生一个长度为10的局限0-100不反复的数组
function random() {
var arr = []
for (var i = 0; i < 100; i++) {
//天生轮回100次,天生100个数字。该要领最大的弊病,为了防止有反复的状况致使数组不足10个元素,所以天生较多的数字
var num = Math.floor(Math.random() * 100) //天生0-100的随机整数
if (arr.length == 0) {
arr.push(num) //数组为空时直接放入数组
} else {
for (var j = 0; j < arr.length; j++) {
//轮回已存在的数组
if (arr.join(',').indexOf(num) < 0 && arr.length < 10) {
//推断已存在数组中是不是已有刚天生的数字,如没有且数组长度不足10才将num放入arr
arr.push(num) //如许又会致使天生的大部分数字被arr.length <= 10消除掉了,糟蹋机能
}
}
}
}
return arr
}
let set = new Set()
while (set.size < 10) {
//若干
set.add(Math.round(Math.random() * 10) + 0) //最大值,最小值
}
let arr4 = Array.from(set)
console.log(arr4)
var arr5 = new Array()
while (arr5.length < 10) {
var num = Math.round(180 * Math.random()) + 20
var exists = false
for (var i = 0, l = arr5.length; i < l; i++) {
if (arr5[i] == num) {
//推断是不是已存在
exists = true //存在的话将true传给exists
}
}
if (!exists) {
//如今exist是true,!exists就是false,所以不实行这个if下面代码。
arr5.push(num)
}
}
console.log(arr5)