theme: condensed-night-purple
highlight: a11y-dark
这是我参与「第四届青训营 」笔记创作活动的的第21天
js运行三部曲
语法分析
预编译
解释执行
语法分析
通篇扫描,看是否有语法错误,不执行
预编译
前奏
imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象window所有 eg:a = 123;
js eg:var a = b = 123; //b未声明,window.b可以访问 //a声明了,window.a不可访问
一切声明的全局变量,全是window的属性
js var a = 123; //相当于 window { a : 123 }
window 就是全局
下一次访问a时,会在 window 里找有没有a
在全局的范围内访问 a 就是访问window.a js var a = 123; console.log(a) --> console.log(window.a)
预编译
函数声明整体提升
系统总是会把函数声明提到程序逻辑最前面
变量 声明提升
变量声明:var a;
系统会把变量声明提到程序最前面
预编译发生在函数执行的前一刻
函数体系里的预编译: 1. 创建AO对象(Activation Object 执行期上下文) 2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined 3. 将实参值和形参统一 4. 在函数体里面找函数声明,值赋予函数体
```js function fn(a) { console.log(a); //out: function a() {}
var a = 123; //预编译时将`var a`提前,执行`a = 123;`赋值语句
console.log(a); //out: 123
function a() {}
console.log(a); //out: 123
var b = function () {}
console.log(b); //out: function () {}
function d () {}
}
fn(1);
// 预编译过程 1.AO{
}
2.AO{ a : undefined, b : undefined, }
3.AO{ a : 1, b : undefined, }
4.AO{ a : function a() {}, b : undefined, d : function d () {} }
//执行过程 AO{ a : 123, b : undefined, d : function d () {} } AO{ a : 123, b : function () {}, d : function d () {} } ```
练习
js function test(a,b) { console.log(a); c = 0; var c; a = 3; b = 2; console.log(b); function b () {} function d () {} console.log(b); } test(1); /*out: 1 2 2 */ AO { a :undefined --> 1 b :undefined --> function b () {} c :undefined d :undefined --> function d () {} } AO { a :1 --> 3 b :function b () {} --> 2 c :undefined --> 0 d :function d () {} }
js function test (a,b) { console.log(a); console.log(b); var b = 234; console.log(b); a = 123; console.log(a); function a() {} var a; b = 234; var b = function () {} console.log(a); console.log(b); } test(1); /*out: ƒ a() {} undefined 234 123 123 ƒ () {} */
全局预编译: 1. 创建GO对象(Global Object 执行期上下文) 2. 找变量声明,将变量作为GO属性名,值为undefined 3. 找函数声明,值赋予函数体
GO === window
```js GO { test : undefined --> function test () { ··· } --> 123 }
console.log(test); function test (test) { console.log(test); var test = 234; console.log(test); function test() {} }
AO { test : undefined --> 1 --> function test() {} --->234 }
test(1); var test = 123;
/*out: function test () { ··· } function test () {} 234 */ ```
练习
js var global = 100; function fn() { console.log(global); //out: 100 } fn();
```js GO { golbal : undefined ---> 100 fn : function () { ··· } } global = 100; function fn() { console.log(global); global = 200; console.log(global); var global = 300; } AO { global : undefined ---> 200 ---> 300 } fn(); var global;
//out: undefined // 200 ```
```js GO { a :undefined ---> 10 test : function () { ··· } c : 234 }
function test () { console.log(b); if(a) { var b = 100; } c = 234; console.log(c); } var a; AO { b : undefined } test(); a = 10; console.log(c);
//out:undefined // 234 // 234 ```
```js GO { a : undefined --> 100 demo : function demo () {} f : 123 } a = 100; function demo(e) { function e () {} arguments[0] = 2; console.log(e); if(a) { var b = 123; function c () { //猪都能做出来 } } var c; a = 10; var a; console.log(b); f = 123; console.log(c); console.log(a); } var a;
AO {
a : undefined --> 10
b : undefined
c : undefined
e : undefined --> 1 --> function e () {} --> 2
}
demo(1);
console.log(a);
console.log(f);
/* 2 undefined undefined (function c() {}) ----if里不能声明function 10 100 123 */
```
作用域
作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域
全局、局部变量 函数里的是局部变量,外面的是全局变量 函数内部可以使用函数外面的变量
作用域的访问顺序 函数里面可以访问函数外的变量,但函数外部无法访问函数内部的变量
作用域精解 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。 一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
查找变量:从作用域链的 顶端 依次向下查找
[[scope]]:每个JavaScript函数都是一个对象,对象有属性,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式连接,我们把这种链式连接叫做作用域链
```js function a () { function b () { var b = 234; } var a = 123; b(); } var global = 100; a();
// a被定义时 a.[[scope]] --> 0 : GO
// a执行时 a.[[scope]] --> 0 : aAO
// 1 : GO
// b被定义时 b.[[scope]] --> 0 : aAO
// 1 : GO
// b执行时 b.[[scope]] --> 0 : bAO
// 1 : aAO
// 2 : GO
```
```js function a() { function b() { function c() {
}
c();
}
b();
} a(); ```
闭包
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏
js function a() { function b() { var bbb = 234; document.write(aaa); } var aaa = 123; return b; } var glob = 100; var demo = a(); demo(); // out: 123
```js function test () { var arr = []; for(var i = 0;i < 10;i ++){ arr[j] = function() { console.log(j); }
}
return arr;
}
var myArr = test();
for(var j = 0;j < 10;j ++){
myArr[j]();
}
// 因为i在test函数的AO里,arr里的函数共用一个i,所以执行后打印10个10
// 解决方法 :用闭包解决闭包,在函数自己的AO里可以取到想要的值
// 使用立即执行函数,将i的值传参进去
for(var i = 0;i < 10;i ++){
(function (j) {
arr[j] = function() {
console.log(j);
}
}(i))
}
```
不用return实现 ```js var demo; function test () { var x = 123; function a () { console.log(x) } demo = a; } test(); demo();
```
闭包的作用
实现共有变量 eg:函数累加器
js function add() { var num = 0; function a () { console.log(++ num); } return a; } var myAdd = add(); myAdd(); myAdd(); myAdd();
可以做缓存(存储结构 eg:
- 可以实现封装,属性私有化 eg:Person();
```js function Deng(name,wife) { var prepareWife = 'xiaozhang';
this.name = name;
this.wife = wife;
this.divorce = function () {
this.wife = prepareWife;
}
this.changPrepareWife = function (target) {
prepareWife = target;
}
this.sayPrepareWife = function() {
console.log(prepareWife);
}
} var deng = new Deng ('deng','xiaoliu'); ```
- 模块化开发,防止污染全局变量
闭包的防范
闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生
原型
定义
原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。 原型也是对象
```js //Person.prototype ----原型 产生时就已定义好 //Person.prototype = {} 是祖先 Person.prototype.name = 'hehe'; function Person() {
} var person = new Person(); ```
自己和原型的属性撞了,用自己的
利用原型特点和概念,可以提取共有属性
对象如何查看原型 —— > 隐式属性proto
xxx:隐式命名规则,系统命名 _xxx :私人的属性,提醒他人尽量别碰这个属性
js Person.prototype.name = 'abc'; function Person() { //var this = { // __proto__ : Person.prototype 存放队形的原型,将原型和自己连接到一起 //}; } var person = new Person(); person.name; //当对象没有这个属性时,就会在__proto__里找原型里是否有该属性
构造函数内部原理 1. 在函数体最前面隐式的加上this = {} 2. 执行this.xxx = xxx; 3. 隐式的返回this this不为空
js var this = { // __proto__ : Person.prototype (可以修改
```js Person.prototype.name = 'sunny'; function Person () {
} var person = new Person(); Person.prototype = { name : 'cherry'; } //person.name : sunny; // //Person.prototype.name 是新加属性 //而 这种写法改了原型
var obj = {name : 'a'}; var obj1 = obj; obj = {name : 'b'}; //obj1.name = 'a'; //obj.name = 'b';
Person.prototype.name = 'sunny'; function Person () {
} Person.prototype = { name : 'cherry'; } var person = new Person();
//person.name : cherry; ```
- 对象如何查看对象的构造函数 —— > constructor (可以自己更改
原型的增删改查
```js Person.prototype.lastName = "Deng"; function Person (name) { this.name = name; } var person = new Person('xiaoming');
person.lastName = 'Zhang'; //只能在person的属性上改,会给它加一个属性lastName Person.prototype.lastName = 'Wang'; //可修改原型属性
//删除 delete Person.prototype.lastName;
// Person.prototype = { name : "BMW"; } ```
原型链
- 如何构成原型链
原型还有原型 原型链的连接点是proto 原型链的终端为Object.prototype
- 原型链上属性的增删改查
本人修改,后代无法更改
特例:修改 引用值可以修改
js son.num ++; //101 //son.num = son.num + 1; father.num //100
```js Person. prototype = { name : "a", sayName : function () { console.log (this.name) ; } } function Person ( ) {
}
var person = new Person() ;
person.sayName() // a
// a.sayName( )
// sayName里面的this指向是,谁调用的这个方法,this就是指向谁
```
绝大多数对象的最终都会继承自Object.prototype
Object.creat(null)
无原型Object.create(原型) ```js //var obj = Object.create(原型) Person.prototype.name = 'sunny'; function Person() {
} var person = Object.create(Person.prototype); ```
Math.ceil()
向上取整Math.floor()
向下取整 eg:js Math.ceil(123.234) ----> 124 Math.floor(123.99) ----> 123
Math.random()
随机产生0-1之间的数 随机产生0-100之间的数Math.floor(Math.random()*100);
Math.random().toFixed(2)*100
会有精度问题
- 可正常计算的范围 : 小数点 前16位,后16位
call/apply
作用:改变this指向
区别:后面传的参数形式不同
call:需要把实参按照形参的个数传进去 apply:需要传一个arguments
还有一种改变this指向 :bind bind 会将改变this后的函数传递回来,不立即执行,在执行的时候传递参数
```js function test () {
}
test() ----> test.call();
function Person (name,age) {
//this == obj
this.name = name;
this.age = age;
}
var person = new Person('Deng',100);
var obj = {};
Person.call(obj,'Deng',100); // 将Person里所有的this都改为obj,默认指向window
// obj :{name: "Deng", age: 100}
```
- 使用call的对象必须是new出来的
```js function Person (name,age,sex) { this.name = name; this.age = age; this.sex = sex; }
function Student (name,age,sex,grade) {
Person.call(this,name,age,sex);
//Person.apply(this,[name,age,sex]);
this.grade = grade;
}
var student = new Student('aaa',30,'male',6);
```