JS笔记

1.数据类型

(1)七种基本数据类型 Boolean null 一个表明 null 值的特殊关键字 undefined 表示变量未定义时的属性 Number 整数或浮点数 BigInt 任意精度的整数,可以安全地存储和操作大整数 String Symbol 实例是唯一且不可改变的数据类型

(2)引用类型 - 对象:Object、Array - 函数 - 三种基本包装类型: String、Number、Boolean

2.字符串转换为数字

(1)parseInt(x,10),parseFloat()

1
2
> parseInt("101",2)
> 5
(2)一元加法运算符
1
2
3
"1.1" + "1.1" = "1.11.1"
(+"1.1") + (+"1.1") = 2.2
// 注意:加入括号为清楚起见,不是必需的。

3.被计算为false的值

  • false
  • undefined
  • null
  • 0
  • NaN
  • 空字符串("")

4.promise

promise生命周期

原型链

1
2
3
4
let Person = function(){
this.name = "Jhon"
}
let person = new Person()
1
2
3
//Person的原型对象
console.log(Person.prototype)
//{constructor: ƒ (),__proto__: Object}

​ 可见Person的原型对象(Person.prototype)包含constructor(构造函数)和__proto__两个属性,其中constructor指向Person构造函数,__proto__指向Object对象。

原型链指向如下图:

原型链

Person构造函数的原型(prototype)指向它的原型对象,即Person.prototype。这个原型对象的constructor属性指向(回)这个构造函数Person,另一个属性__proto__指向Object的原型对象Object.prototype。

5.函数作用域链

6.逻辑与、或[理解]

1
2
var a = "Cat" && "Dog"; //return Dog
var b = "Cat" || "Dog"; //return Cat

作为逻辑表达式进行求值是从左到右,它们是为可能的“短路”的出现而使用以下规则进行测试:

1
2
false && anything    // 被短路求值为false
true || anything // 被短路求值为true
逻辑的规则,保证这些评估是总是正确的。请注意,上述表达式的anything部分不会被求值,所以这样做不会产生任何副作用。 简化if
1
2
3
4
5
6
7
8
let a = 1
let b;
if(a==1){
b = a + 1;
}
//上式可写为:
a==1&&(b = a + 1);
console.log(b)

7.in 关键词使用

1
2
3
4
5
6
7
8
9
10
11
使用格式 (变量 in 对象)
当对象是数组是,变量指的是数组索引
当对象是对象时,变量指的是对象属性
let a = ["er","ygbn","t"]
if (2 in a){
//
}
let b = {a:34,b:"ui"}
if (a in b){
//
}

8.webpack打包命名问题

1
W/7r => js/W/7r  //斜杠会被解析为子路径

所以在部署到容器的时候: COPY dist/js/* 这样就不会复制 W/7r 到容器路径,导致错误 COPY dist/js/ 去掉* 包含 W/* 就没错了

9.this对象

MDN

  • 在标准函数中,this 引用的是把函数当成方法调用的上下文对象,这时候通常称其为 this 值(在 网页的全局上下文中调用函数时,this 指向 windows)

  • 在箭头函数中,this 引用的是定义箭头函数的上下文

当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。

(1)全局上下文

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

1
2
3
4
5
6
7
8
9
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b) // "MDN"
console.log(b) // "MDN"

(2)函数上下文

​ 在函数内部,this的值取决于函数被调用的方式。

非严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向全局对象,浏览器中就是 window

1
2
3
4
5
6
7
8
function f1(){
return this;
}
//在浏览器中:
f1() === window; //在浏览器中,全局对象是window

//在Node中:
f1() === globalThis;

​ 严格模式下,如果进入执行环境时没有设置 this 的值,this 会保持为 undefined,如下:

1
2
3
4
5
6
function f2(){
"use strict"; // 这里是严格模式
return this;
}

f2() === undefined; // true

(3)类上下文 this在类中的表现与函数类似,因为类本质上也是函数,但有一些区别和注意事项。 在类的构造函数中,this是一个常规对象。类中所有非静态的方法都会被添加到this的原型中:

1
2
3
4
5
6
7
8
9
10
11
class Example {
constructor() {
const proto = Object.getPrototypeOf(this);
console.log(Object.getOwnPropertyNames(proto));
}
first(){}
second(){}
static third(){}
}

new Example(); // ['constructor', 'first', 'second']

注意:静态方法不是 this 的属性,它们只是类自身的属性。

(4)派生类

不像基类的构造函数,派生类的构造函数没有初始的 this 绑定。在构造函数中调用 super() 会生成一个 this 绑定,并相当于执行如下代码,Base为基类:

1
this = new Base()

10.call()、apply()、bind()

这三个函数都是用来改变某个函数运行时上下文,即改变函数内部 this 的指向。

(1)call、apply、bind用法之改变函数作用域[上下文]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var age = 22;
var obj = {
name: "橘子",
age: this.age,
say: function(job, address) {
console.log(
`${this.name} age is ${this.age}, job is ${job}, address is ${address}`
);
}
};
var x = {
name: "天空",
age: "35"
};
// 正常调用
obj.say(); //橘子:22
// call调用 注意参数
obj.say.call(x, "前端", "北京"); //天空 age is 35, job is 前端, address is 北京
// apply 注意参数是个数组
obj.say.apply(x, ["前端", "北京"]); //天空 age is 35, job is 前端, address is 北京
// bind 注意bind 返回的是一个函数,需要手动调用
obj.say.bind(x, "前端", "北京")(); //天空 age is 35, job is 前端, address is 北京
obj.say.bind(x,["前端", "北京"])(); //天空 age is 35, job is 前端,北京, address is undefined

这三种调用"劫持"了 obj 的 say 方法,注意:

  • call调用参数传递逗号分隔,第一个是上下文对象。

  • apply调用参数传递是一个数组。

  • bind调用返回的是一个函数。

(2)call、apply、bind用法之实现继承[上下文]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 父类
function Animal(name,size){
this.walk = function(){
return `${name} is ${size}, but can walk.`
}
}
// 子类
function Cat(name, size, color){
//这里以call为例,apply和bind同理
Animal.call(this, name, size);
this.color = color;
}
let papy = new Cat("titi", "small", "pink")
console.log(papy.walk()) //titi is small, but can walk.

(2)手写call方法

1
2
3
4
5
6
7
8
9
10
//(3)手写call方法
Function.prototype.mycall = function(context = window) {
const ctx = context || window;
ctx.fn = this;
let args = [...arguments].slice(1);
let res = ctx.fn(...args);
delete ctx.fn;
return res;
};
obj.say.mycall(x, "mycall", "北京");//天空 age is 35, job is mycall, address is 北京

(3)手写apply方法

1
2
3
4
5
6
7
8
9
10
//(4)手写apply方法
Function.prototype.myapply = function(context = window) {
const ctx = context || window;
ctx.fn = this;
let args = arguments[1] || [];
let res = ctx.fn(...args);
delete ctx.fn;
return res;
};
obj.say.myapply(x, ["myapply", "北京"]);//天空 age is 35, job is myapply, address is 北京

11.new运算符

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new

语法

1
2
3
4
new constructor[([arguments])]
参数:
constructor:一个指定对象实例的类型的类或函数
arguments:一个用于被 constructor 调用的参数列表。

描述

new 关键字会进行如下的操作:

1.创建一个空的简单的JavaScript对象(即{});

2.链接该对象(设置该对象的constructor)到另一个对象;

3.将步骤一新建的对象作为this的上下文;

4.如果该函数没有返回对象,则返回this。

创建一个自定义对象需要两步:

1.通过编写函数定义对象类型

2.通过new来创建对象实例

当代码 new Foo(...) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

12.类

13.BOM浏览器对象模型

判断一个变量是数组

(1)instanceof判断引用类型 > 判断某个构造函数的prototype属性所指向的对象是否存在于另外一个要检测对象的原型链上, > > 语法:object instanceof constructor

eg:

1
2
3
4
5
const a = [];
const b = {};
console.log(a instanceof Array);//true
console.log(a instanceof Object);//true,在数组的原型链上也能找到Object构造函数
console.log(b instanceof Array);//false
(2)constructor判断 也可用于判断某个实例的构造函数:p.constructor === Person
1
2
3
const a = [];
console.log(a.constructor) //function Array(){ [native code] }
console.log(a.constructor === Array) //true
eg:
1
2
3
4
5
6
7
const a = [];
console.log(a.constructor == Array);//true
const b = [];
//constructor属性被修改之后,无法判断了。
b.contrtuctor = Object;
console.log(b.constructor == Array);//false
console.log(b.constructor == Object);//true

(3)用Object的toString方法 再加上call或者apply方法来改变toString方法的执行上下文来判断,每一个继承自Object的对象都拥有toString的方法。如果一个对象的toString方法没有被重写过的话,那么toString方法将会返回"[object type]"。

1
2
3
4
5
6
7
8
9
const a = ['Hello','Howard'];
const b = {0:'Hello',1:'Howard'};
const c = 'Hello Howard';
Object.prototype.toStrisng.call(a);//"[object Array]"
Object.prototype.toString.call(b);//"[object Object]"
Object.prototype.toString.call(c);//"[object String]"
Object.prototype.toString.apply(a);//"[object Array]"
Object.prototype.toString.apply(b);//"[object Object]"
Object.prototype.toString.apply(c);//"[object String]"

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const isArray = (something)=>{
return Object.prototype.toString.call(something) === '[object Array]';
}
cosnt a = [];
const b = {};
isArray(a);//true
isArray(b);//false

//作死重写了toString方法
Object.prototype.toString = () => {
alert('你吃过了么?');
}
//调用String方法
const a = [];
Object.prototype.toString.call(a);//弹框你吃过了么?
(4)用Array对象的isArray方法判断
1
2
const a = [];
Array.isArray(a);//true
(5)其他:typeof

注意:typeof 只能用来判断基本类型和函数。对于数组、对象和String()等包装类型均返回Object,不能辨别。

函数柯里化

event loop

https://juejin.im/post/6844903670291628046#heading-4

宏任务队列,macrotask || tasks - setTimeout - setInterval - setImmediate(node独有) - requestAnimationFrame(浏览器独有) - I/O - UI rendering(浏览器独有) 微任务队列,microtask || jobs - process.nextTick(node独有) - Promise - Object.observe - MutationObserve -

优先运行微任务队列的任务

类数组转数组

类数组是具有length属性,但不具有数组原型上的方法。 比如说arguments,DOM操作返回的结果就是类数组。类数组转数组方法:

1
2
3
Array.from(arguments)
Array.prototype.slice.call(arguments)
[...arguments]

节流

一段时间内只能触发一次,如果这段时间触发多次时间,只有第一次生效会触发回调函数,一段时间过后才能再次触发(n秒内第一次生效)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<button onclick="invoke()">click</button>
<script>
function sayHi() {
console.log("hihi");
}

function throttle(fn, delay) {
let flag = true;
// 返回一个包装过的函数
return function() {
if (flag) {
flag = false;
setTimeout(() => {
console.log("hahah");
flag = true;
}, delay);
return fn();
}
};
}
// 初始化节流函数
var invoke = throttle(sayHi, 3000)
</script>

防抖

在事件被触发时,延迟n秒再触发回调函数,如果n秒又触发了事件,则会重新开始计算时间(n秒内最后一次生效)

Virtual DOM(虚拟DOM)算法

参考https://juejin.im/entry/5aedcfa351882506a36c664c 1. 用JavaScript对象结构表示DOM树的结构;然后用这个树构建一个真正的DOM树,插入文档中 2. 当状态变更的时候,重新构造一颗新的对象树;然后用新的树和旧的树进行比较,记录两个树的差异 3. 把步骤2所记录的差异应用到步骤1所构建的真正的DOM树上,视图更新 Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。 ## 继承 1.原型与继承关系

原型与实例的关系可以通过两种方式来确定。

  • instanceof 如果一个实例的原型链中出现过相应的构造函数,则instanceof返回true
  • isPrototypeOf()方法 原型链中的每个原型都可以调用这个方法 eg:Object.prototype.isPrototypeOf(instance) SubType.prototype.isPrototypeOf(instance)

2.原型链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 父类
function SuperType() {
this.colors = ["red", "blue", "green"];
this.size = "small";
}
// 子类
function SubType() {}
//继承SuperType
SubType.prototype = new SuperType();

let instance1 = new SubType();
// 修改instance1的colors属性
instance1.colors.push("black");
instance1.size = "medium";
console.log(instance1.colors); //red,blue,green,black
console.log(instance1.size);
let instance2 = new SubType();
// instance2的colors属性也被修改,可见colors属性是被所有实例共享的
console.log(instance2.colors); //red,blue,green,black
console.log(instance2.size);

缺点:

  1. 原型中包含的引用值会在所有实例间共享
  2. 子类型在实例化时不能给父类型的构造函数传参

3.盗用构造函数

使用apply()和call()方法以新创建的对象为上下文执行构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 父类
function SuperType() {
this.colors = ["red", "blue", "green"];
}
// 子类
function SubType() {
// 继承superType
SuperType.call(this);
}

let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"

let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"

​ ==相当于新的 SubType 对象上运行了 SuperType()函数中的所有初始化代码。结果就是每个实例都会有自己的 colors 属性。==

盗用构造函数实现继承的另一个好处就是可以传递参数给父类构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
function SuperType(name) {
this.name = name;
}
function SubType() {
// 继承 SuperType 并传参 SuperType.call(this, "Nicholas");
SuperType.call(this, "zhunh")
// 实例属性
this.age = 29;
}
let instance = new SubType();
console.log(instance.name); // "Nicholas"; console.log(instance.age); // 29
console.log(instance.age);

缺点:

  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承父类原型对象上的属性和方法
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

4.组合继承

综合原型链和构造函数继承的优点,基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
// let spt = new SuperType()
// console.log(spt.__proto__.__proto__.constructor)
// console.log(SuperType.prototype)
function SubType(name, age) {
// 继承属性
SuperType.call(this, name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function() {
console.log(this.age);
};

let instance1 = new SubType("zhunh", 22);
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //zhunh
instance1.sayAge(); //22

let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); //"red,blue,green"
instance2.sayName(); //reg
instance2.sayAge() //27

5.原型式继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 核心函数 */
function object(o) {
function F() {}
F.prototype = o;
return new F();
}

let person = {
name: "zhunh",
friends: ["Tom, Jack, Van"]
};
let anotherPerson = object(person);
console.log(anotherPerson);

let yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

person.friends 不仅是 person 的属性,也会跟 anotherPerson 和 yetAnotherPerson 共享.

​ ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个 参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时, Object.create()与这里的 object()方法效果相同:

1
2
let anotherPerson = Object.create(person);
let yetAnotherPerson = Object.create(person);

原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住, 属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。 6.寄生式继承

Object.create和new的区别

new 关键字创建的对象会保留构造函数的属性,而Object.create创建的对象不会

1
2
3
4
5
6
7
8
var Base = function(){
this.name = "一路向北"
}
Base.prototype.age = 25
var a = new Base()
var b = Object.create(Base)
console.log(a)
console.log(b)

字节面试题:

1.event loop

2.BFC IFC

3.岛屿问题

4.N叉树最大深度

5.三栏布局

位(bit) :计算机内部数据 存储的最小单位

字节(byte):计算机中数据处理的基本单位,1B(byte,字节) = 8bit(位)

ASCIIS码:

1个英文字母 = 1个字节的空间

1个中文汉字 = 2个字节的空间

事件