一些js知识| 青训营笔记


theme: condensed-night-purple

highlight: a11y-dark

这是我参与「第四届青训营 」笔记创作活动的的第21天

js运行三部曲

  • 语法分析

  • 预编译

  • 解释执行

语法分析

通篇扫描,看是否有语法错误,不执行

预编译

前奏

  1. imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象window所有 eg:a = 123; js eg:var a = b = 123; //b未声明,window.b可以访问 //a声明了,window.a不可访问

  2. 一切声明的全局变量,全是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 */
```

作用域

  1. 作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域

  2. 全局、局部变量 函数里的是局部变量,外面的是全局变量 函数内部可以使用函数外面的变量

  3. 作用域的访问顺序 函数里面可以访问函数外的变量,但函数外部无法访问函数内部的变量

作用域精解 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。 一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。

查找变量:从作用域链的 顶端 依次向下查找

[[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();

```

闭包的作用

  1. 实现共有变量 eg:函数累加器 js function add() { var num = 0; function a () { console.log(++ num); } return a; } var myAdd = add(); myAdd(); myAdd(); myAdd();

  2. 可以做缓存(存储结构 eg:

在这里插入图片描述

  1. 可以实现封装,属性私有化 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'); ```

  1. 模块化开发,防止污染全局变量

闭包的防范

闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生

原型

定义

原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。 原型也是对象

```js //Person.prototype ----原型 产生时就已定义好 //Person.prototype = {} 是祖先 Person.prototype.name = 'hehe'; function Person() {

} var person = new Person(); ```

自己和原型的属性撞了,用自己的

  1. 利用原型特点和概念,可以提取共有属性

  2. 对象如何查看原型 —— > 隐式属性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; ```

  1. 对象如何查看对象的构造函数 —— > 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"; } ```

原型链

  1. 如何构成原型链

原型还有原型 原型链的连接点是proto 原型链的终端为Object.prototype

  1. 原型链上属性的增删改查

本人修改,后代无法更改

特例:修改 引用值可以修改 在这里插入图片描述

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就是指向谁

```

  1. 绝大多数对象的最终都会继承自Object.prototype Object.creat(null)无原型

  2. 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);

```

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_50945128/article/details/129377806