JS
变量提升
fn() // 由于函数表达式是不能进行变量提升,所以这里执行的 打印2的
var fn = function () {
console.log(1)
}
function fn() {
console.log(2)
}
fn() //1
var func1 = {
name: 'a',
test: function () {
console.log(this.name);
}
}
name = 'global'
func1.test() //a
func = func1.test //相当于 func = function(){.....} 这里的this指向 window
func() // global
function func2(){
var name = 'func2'
func()
}
func2() //global
let obj = {
sangfor : 100,
log() {
sangfor = 200; //在这里,没有使用 var或者let来定义变量,这就是一个陷阱。没有使用这两个的话,js就会默认是使用var在外部定义的,然后再内部更改值。所以这个变量变成了全局变量
console.log(this.sangfor);
}
}
obj.log() // 100
let { log } = obj
log() //这个函数this就是指向全局 200
let x = 10;
let foo = () => {
// 这里访问 x 就会在当前作用域内找 x
// 如果没有下面的 let x = 20
// 当前作用域就找不到,然后就会继续找外层变量,最后输出 10
// 但是下面有声明,声明会提升到当前代码块最前面
// 这个时候 x 还没有初始化,存在暂存死区中,访问就会抛出 ReferenceError
console.log(x);
let x = 20;
x++;
};
foo();
//如果把里面的 let 换成 var 就不会报错,显示 undefined
//就是因为 let 是在编译时才会初始化,var 在声明时就会初始化
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
//变量提升,相当于
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
//Goodbye Jack
var a,b;
(function(){
alert(a); //这是第一个输出的,先在局部没找到a变量,然后去全局找,找到了但没定义,输出undefined
alert(b); //这是第二个输出的,其他同上
var a=b=3; //拆解成var a=b; b=3; 然后,b=5前面没有var,相当于声明为全局变量
alert(a); //这是第三个输出,局部变量a=3
alert(b); //这是第四个输出,全局变量b=3
})(); //这个函数体已经执行完毕,里面的内存已经被垃圾回收器回收,局部变量a销毁
alert(a); //这是第五个输出,全局变量a=undefined
alert(b); //这是第六个输出,全局变量b=3
故正确答案是undefined,undefined,3,3,undefined,3;
let a = { n: 1 };
let b = a; //a和b指向同一个对象
a.x = a = { n: 2 };
// 由于.的优先级比 = 高,所以先执行 a.x,于是现在的a和b都是 { n: 1,x:undefined }
// 然后执行 a = {n:2},于是a指向了 {n:2},已经不再是之前的a
// 最后执行 a.x = a, 所以b和之前的a指向 {n:1,x:{n:2}}
console.log(a); //{n:2}
console.log(b); //{n:1,x:{n:2}}
var a = 10;
function a(){}
console.log(typeof a) //number
//由于函数声明的优先级比var要高,所以等价于
function a(){}
var a;
a = 10
console.log(typeof a) //所以打印的结果是 number
var x=10;
function cals(myNum) {
return x+myNum;
}
cals(7) //17
//等价于
function cals(myNum) {
return x+myNum;
}
var x;
x=10
cals(7)
var foo=”Hello”;
(function(){
var bar=”World”;
alert(foo+bar); // hello world
})();
alert(foo+bar); //报错
//(function(){})()这是一个匿名自执行函数,在函数内部声明的变量bar为局部变量,函数外部无法调用,所以第二次打印报错;第一次打印时。函数通过作用域链可以访问到外部的全局变量foo;因此第一次打印为hello world
(function() {
var x=foo();
var foo=function foo() {
return "foobar"
};
return x;
})();
//类型错误
//变量提升
//等价于
var foo;
var x = foo();
foo = function foo() {...}
//当执行到 x = foo() 时,由于foo未被定义为函数
function checkAge(age) {
if (age < 18) {
const message = "Sorry, you're too young."
} else {
const message = "Yay! You're old enough!"
}
return message
}
console.log(checkAge(21)) //ReferenceError
//const 和 let 声明的变量是具有块级作用域的,块是大括号( {} )之间的任何东西, 即上述情况 if / else 语句的花括号。 由于块级作用域,我们无法在声明的块之外引用变量,因此抛出 ReferenceError
基本数据类型的转换
1、转换成
Boolean
-
空字符
=>
false
,
非空字符串
=>
true
-
0
、
NaN
=>
false
,
除了 0 和 NaN 以外
=>
true
- object 为 null 时,会转换为 false ,如果 object 不为 null ,则都会转换成 true
这里的object为null的意思是说,object = null这种形式
而 object = [] or object = {}都不是 null的意思
- 任何 Function 类型都会转换为 true
- Null => false
- Undefined => false
2、转换成
Number
-
如果值为
null
,则返回
0
-
如果值为
undefined
,则返回
NaN
-
字符串不包含任何字符,或为连续多个
空格
,则会转换为
0
- 如果是对象类型,则会调用对象的 valueOf( ) 函数获取返回值,并且判断返回值能否转换为 Number 类 型,如果不能,会调用对象的 toString( ) 函数获取返回值,并且判断是否能够转换为 Number 类型。如果也不 满足,则返回 NaN
运算符
-
如果比较的值是字符串类型,则判断
每个
字符是否相等,如果全部相等,返回 true ,否则返回`false` - 如果比较的值都是引用类型,则比较的是引用类型的地址,当两个引用指向同一个地址时,则返回 true ,否则 返回`false`
- null == undefined比较结果是true,除此之外,null、undefined和其他任何结果的比较值都为false
- NaN和其他任何类型比较永远返回false(包括和他自己)
- 如果比较的是字符串和数值类型数据,则会将字符串转换为数值后再进行比较,如果转换后的数值是相等的 则返回 true ,否则返回 false
- 如果比较的时候,有一方的类型是 boolean 类型,会将 boolean 类型进行转换, true 转换为1, false 转 换0,然后再进行比较
console.log(([]) ? true : false);// => 数组是对象,这里不是等于null,所以是true
console.log(([] == false ? true : false));// => console.log(0==0?true:false);
console.log(({} == false) ? true : false);// => console.log((NaN==0)?true:false);
console.log('11' > '2'); // 字符串对比 是一个字符对应对比的 1>2 ==>false
console.log('0' == true); // 布尔值与其他值比较时,转化成数字类型===> 1 然后字符串跟数字类型比较 字符串转成数字类型
console.log([] == []);
console.log([] == false); //布尔值和其他相比较的话,会转化成Number类型 []转换为 0
console.log(NaN == false); //NaN和其他任何类型比较永远返回false(包括和他自己)。
// null == undefined比较结果是true,除此之外,null、undefined和其他任何结果的比较值都为false。
console.log(undefined == '0');
console.log(null == 0);
console.log(null == undefined);
typeof运算符
类型 |
结果 |
Number、 NaN |
number |
String | string |
Object、 null 、Array |
object |
Function | function |
Undefined | undefined |
Boolean | boolean |
Symbol | symbol |
function Sangfor(name) {
return this.name = name
}
console.log([0, '0', [], {}, null].map(i => typeof new Sangfor(i).name)) //[ 'number', 'string', 'undefined', 'undefined', 'object' ]
console.log(new Sangfor([]).name); //new Sangfor([]) 返回了 [] [].name 肯定是undefinded
console.log(new Sangfor(null).name); // null
console.log(typeof []); //object
console.log(typeof {}); //object
console.log(typeof null); //object
const name = "Lydia Hallie";
console.log(!typeof name === "object"); //false
console.log(!typeof name === "string"); //false
typeof name 返回 "string" 。字符串 "string" 是一个 truthy 的值,因此 !typeof name 返回一个布尔值false。false === "object" 和 false === "string" 都返回 false 。
(如果我们想检测一个值的类型,我们应该用 !== 而不是 !typeof )
对象中的get和set是当对象赋值和取值自动调用的
let oo = {
get sangfor() {
console.log('get')
},
set sangfor(v) {
console.log('set')
},
}
oo.sangfor = '100'
console.log(oo.sangfor) //set get undefined
toString():对象被表示为文本值时或者当以
期望
字符串的方式引用对象时,该方法被自动调用
valueOf():如果对象
没有原始值
,valueOf() 就会返回对象自身
let x = {
toString(){
return 20
},
valueOf(){
return 30
}
}
console.log(x == '20'); //因为这里没有原始值,所以在这里调用的是 valueOf 而不是 toString
console.log(x == 30);
for…in… 和 for…of…的区别
- for…in…:返回的值是键值对中的键
- for…of…:返回的值是键值对中的值,但是要有iterator接口
const obj = {
a: 1,
b: 2,
c: 3
}
for (let i in obj) {
console.log(i)
// a
// b
// c
}
for (let i of obj) {
console.log(i)
// Uncaught TypeError: obj is not iterable 报错了
}
Object.defineProperty 该方法会直接在一个对象上定义 一个 新属性,或者修改一个对象的现有属性
let xx = {
sangfor: '100'
};
Object.defineProperty(xx, 'sangfor', {
get() {
return '200'
}
})
xx.sangfor = '300'
console.log(xx.sangfor); //200
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person); //{ name: "Lydia", age: 21 }
console.log(Object.keys(person));//{ name: "Lydia", age: 21 }
//defineProperty 方法给对象添加了一个属性之后,属性默认为 不可枚举
原型链
let Base = {
name: 'base'
}
let A = function () {
this.name = 'a'
}
A.prototype = Object.create(Base)
let a = new A();
Base.name = 'new_name';
console.log(a.name); //a
delete a.name;
console.log(a.name); //new_name
function A(x) {
this.x = x;
}
A.prototype.x = 1;
function B(x) {
this.x = x;
}
B.prototype = new A();
const b = new B(3);
delete b.x;
console.log(b.x); //undefined
//b=new B(3)设置的是B方法中的this.x=3 (B),delete b.x也是删除的B上的x,此时b.x就寻找A中的x,首先是构造函数中的this.x (A),没有给这个x赋值,所以返回undefined。
function f1(){}
console.log(f1.prototype,typeof f1.prototype); //{} object
console.log(Object.prototype,typeof Object.prototype);//[Object: null prototype] {} object
console.log(Function.prototype.__proto__) // [Object: null prototype] {}
console.log(Function.prototype) // {}
console.log(Function.prototype.prototype,typeof Function.prototype.prototype);//undefined undefined
console.log(f1.prototype.constructor,typeof f1.prototype.constructor);//[Function: f1] function
var F=function(){};
Object.prototype.a=function(){};
Function.prototype .b=function(){};
var f=new F();
// f 只能取到a 取不到b
// 用new操作符创建的f只是对象,所以只能继承于Object.prototype
Chrome中,1rem =10px
new String( ) 方式生成的字符串是
字符串对象
function showCase(value) {
switch (value) {
case 'A':
console.log('case A');
break;
case 'B':
console.log('case B');
break;
case undefined:
console.log('case undefined');
break;
default:
console.log('Do not Know');
break;
}
}
console.log(new String('A')); // [String:'A']
showCase(new String('A')) //'Do not Know'
JS内置对象
- Array对象
- Date对象
- 正则表达式对象
- string对象
- Global对象
- JSON等
JS内置迭代对象
- String
- Array
- TypedArray
- Map
- Set
JS全局函数
- escape( )
- eval_r( )
- isFinite( )
- isNaN( )
- parseFloat( )
- parseInt( )
- unescape( )
改变作用域链
- with
- try-catch
- eval
javascript异步模式
- 回调函数
- 事件监听
- 发布/订阅
- Promises
RegExp对象方法
- test()
- exec()
- compile()
Generator函数
function* generatorOne() {
yield ['a', 'b', 'c'];
}
function* generatorTwo() {
yield* ['a', 'b', 'c'];
}
const one = generatorOne()
const two = generatorTwo()
console.log(one.next().value) // ['a', 'b', 'c']
console.log(two.next().value) // a
通过
yield
关键字, 我们在
Generator
函数里执行 yield 表达式. 通过
yield*
关键字, 我们可以在一个 Generator 函数里面执行( yield 表达式)另一个 Generator 函数, 或可遍历的对象 (如数组). 在函数
generatorOne
中, 我们通过 yield 关键字 yield 了一个完整的数组 [‘a’, ‘b’, ‘c’] 。函数 one 通 过 next 方法返回的对象的 value 属性的值 ( one.next().value ) 等价于数组 [‘a’, ‘b’, ‘c’]
在函数
generatorTwo
中, 我们使用
yield*
关键字。就相当于函数 two
第一个
yield 的值, 等价于在迭代器中 第一个 yield 的值。数组 [‘a’, ‘b’, ‘c’] 就是这个迭代器. 第一个 yield 的值就是 a , 所以我们第一次调 用 two.next().value 时, 就返回 a 。
回流和重绘
reflow:当render树的一部分或者全部
因为大小边距等问题发生改变而需要重建
的过程,叫做回流
repaint:当诸如颜色背景等
不会引起页面布局变化,
而只需要重新渲染的过程叫做重绘
回流一定伴随着重绘,而重绘却可以单独出现
- display:none指的是元素完全不陈列出来,不占据空间,涉及到了DOM结构,故产生reflow与repaint。
- visibility:hidden指的是元素不可见但存在,保留空间,不影响结构,故只产生repaint
手写AJAX
// 第一步需要new一个XMLHttpRequest对象
const xhr = new XMLHttpRequest()
// 第二步使用传入请求方式,请求路径,同步还是异步
// 最后的一个参数,true是代表异步请求,false是代表同步请求,默认是异步
xhr.open('GET', '/api', true)
// 当状态改变的时候发送请求
xhr.onreadystatechange = function() {
// xhr.readyState的几种状态
// 0 - (未初始化)还没有调用send()方法
// 1 - (载入)已调用send()方法,正在发送请求
// 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
// 3 - (交互)正在解析响应内容
// 4 - (完成)响应内容解析完成,可以在客户端调用
if(xhr.readyState === 4){
// http请求状态码 200代表成功
if(xhr.status === 200){
alert(xhr.responseText)
}
}
}
xhr.send(null)
执行顺序
- 宏任务先执行,setTimeout,他是第二个宏任务,将它扔进宏任务事件队列里先排队
- promise的回调,他是一个微任务,将它扔进微任务事件对列中, 注意:当.then()里没写回调时,当宏任务看
- 第一个宏任务(主程序)执行完,执行全部的微任务,再执行下一个宏任务(settimeout)
- await 之后的代码在 promise 被完成后以 .then 执行
Promise.resolve().then(()=>{
console.log('Promise1') //有回调,微任务 1
})
setTimeout(function(){ //宏任务,放进队列 2
console.log('setTimeout1')
},10)
setTimeout(()=>{ //宏任务,放进队列
console.log('setTimeout2'); // 3
Promise.resolve().then(()=>{
console.log('Promise2') // 4
})
},10)
// Promise1 setTimeout1 setTimeout2 Promise2
console.log(1); //宏任务 1
setTimeout(function () { //宏任务,放进队列
console.log(2); // 6
})
new Promise(function (resolve, reject) {
console.log(3); //宏任务 2
}).then(console.log(4)); //无回调,宏任务 3
console.log(10); //宏任务 4
new Promise(function (resolve, reject) {
setTimeout(function () { //宏任务,放进队列
console.log(5); // 7
});
}).then(console.log(6)); //无回调,宏任务 5
setTimeout(function () { //宏任务,放进队列
new Promise(function (resolve, reject) {
console.log(7); // 8
});
})
// 1 3 4 10 6 2 5 7
setTimeout(function () { console.log(1) }, 0); //宏任务,放进队列 5
new Promise(function (resolve) {
console.log(2) // 宏任务 1
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve()
}
console.log(3) // 宏任务 2
}).then(function () {
console.log(4) //有回调,微任务 4
});
console.log(5); // 宏任务 3
// 2 3 5 4 1
async function async1() {
console.log('1') // 2
await async2();
console.log('2') // 抽出 await 之后的代码放到.then回调 微任务 6
}
async function async2() {
console.log('3') // 3
}
console.log('4') // 1
setTimeout(function(){
console.log('5') // 8
},0)
async1()
new Promise(function (resolve){
console.log('6') // 4
resolve()
}).then(function(){
console.log('7') // 7
})
console.log('8') // 5
// 4 1 3 6 8 2 7 5
函数传递参数都是按
值传递
的
function test(a){
a=a+10;
}
var a=10;
test(a);
console.log(a); // 10
//函数参数仅仅是被传入变量复制给了的一个局部变量,改变这个局部变量不会对外部变量产生影响。
let obj = {};
function changeValue(obj){
obj.name = 'ConardLi';
obj = {name:'code秘密花园'};
}
changeValue(obj);
console.log(obj.name); // ConardLi
函数参数传递的并不是变量的
引用
,而是变量拷贝的副本,当变量是原始类型时,这个副本就是值本身,当变量是引用类型时,这个副本是指向堆内存的地址
function getInfo(member, year) {
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear); //{ name: "Lydia" }, "1997"
函数默认传参情况
const value = { number: 10 };
const multiply = (x = { ...value }) => {
console.log(x.number *= 2);
};
multiply(); //20
multiply(); //40
multiply(value);//20
multiply(value);//20
在ES6中,我们可以使用默认值初始化参数。如果没有给函数传参,或者传的参值为 “undefined” ,那么参数的 值将是默认值。上述例子中,我们将 value 对象进行了解构并传到一个新对象中,因此 x 的默认值为 {number:10} 。
默认参数在调用时才会进行计算,每次调用函数时,都会
创建一个新的对象
。我们前两次调用 multiply 函数且 不传递值,那么每一次 x 的默认值都为 {number:10} ,因此打印出该数字的乘积值为 20 。第三次调用 multiply 时,我们传递了一个参数,即对象 value 。 *= 运算符实际上是 x.number = x.number * 2 的简写,我们修改了 x.number 的值,并打印出值 20 。
第四次,我们再次传递 value 对象。 x.number 之前被修改为 20 ,所以 x.number * = 2 打印为 40 。
function fn(a = 1) {
console.log(a);
}
fn(); //1
fn(undefined); //1
fn(null); //null
fn(false); //false
function getItems(fruitList, ...args, favoriteFruit) {
return [...fruitList, ...args, favoriteFruit]
}
getItems(["banana", "apple"], "pear", "orange") //SyntaxError
//... args 是剩余参数,剩余参数的值是一个包含所有剩余参数的数组,并且只能作为最后一个参数。上述示例中,剩余参数是第二个参数,这是不可能的,并会抛出语法错误。
箭头函数
-
没有
自己的this,而是会继承上层作用域的this,就像其他普通的变量一样 -
不支持
动态改变this值,所以
不可以
通过.call()、.apply()、.bind()方法来重新绑定它的this值 -
没有
arguments对象过度追求 - “单行代码”写法可能会降低代码可读性箭头函数虽然表面上看是匿名的,但它可以根据前面的变量名和属性名自动推断出同名的name属性
-
不可以
被new,也不会像普通函数一样自动拥有prototype属性
const Person = (name="wang",age=10) => {
this.name = name;
this.age = age;
return this.name +' is '+ this.age + 'years old'
}
let result = new Person('zhang',11)
console.log(result)
//这道题要注意哈,咋一看答案就是 zhang is 11 years old
//再认真看一下哈,这个构造函数是使用箭头函数的,所以肯定是报错啊。
let config = {
alert: setInterval(() => {
console.log('Alert!')
}, 1000)
}
config = null
//setInterval 的回调仍然会被每秒钟调用
一般情况下当我们将对象赋值为
null
, 那些对象会被进行 垃圾回收(garbage collected) 因为已经没有对这些对象的
引用
了。然而,setInterval 的参数是一个
箭头函数
(所以上下文绑定到对象 config 了),回调函数仍 然保留着对 config 的引用。只要存在引用,对象就不会被垃圾回收。因为没有被垃圾回收, setInterval 的 回调每1000ms (1s)会被调用一次。
const user = {
email: "my@email.com",
updateEmail: email => {
this.email = email
}
}
user.updateEmail("new@email.com")
console.log(user.email) //my@email.com
updateEmail 函数是一个箭头函数,它没有和 user 对象绑定。这就意味着 this 关键字不会引用到 user 对象,但是会引用到全局对象。 user 对象内部的 email 的值不会更新。当打印 user.email 的时候, 原始 值 my@email.com 被返回。
Setters
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language) // undefined
方法 language 是一个setter。Setters 并不保存一个实际值,它们的使命在于修改属性。当调用方法setter,返回 undefined 。
WebSocket的使用场景 ————->高实时性
- 社交聊天
- 弹幕
- 多玩家游戏
- 协同编辑
- 股票基金实时报价
- 体育实况更新
- 视频会议/聊天
- 基于位置的应用
- 在线教育
- 智能家居
数组方法整理
- 改变原数组
pop()、push()、shift()、unshift()、reverse()、sort()、splice()
- 不改变原数组
concat()、join()、slice()、filter()、reduce()、find()、findIndex()
let newList = [1, 2, 3].push(4)
console.log(newList.push(5)) //Error
//push 方法返回数组的长度,而不是数组本身! 通过将 newList 设置为 [1,2,3].push(4) ,实际上 newList等于数组的新长度: 4 。
//然后,尝试在 newList 上使用 .push 方法。 由于 newList 是数值 4 ,抛出TypeError。
map()函数相关
let array = [,1,,2,,3];
array = array.map((i) => ++i)
// [,2,,3,,4]
// map()会跳过空位,但会保留这个值
var arr = ["1", "2", "3", "4"];
var result = arr.map(parseInt);
console.log(result);
// [1,NaN,NaN,NaN]
// 等效于
var arr = ["1", "2", "3", "4"];
// var result = arr.map(parseInt);
var result = arr.map(function (val, index) {
return parseInt(val, index);
});
console.log(result);
//parseInt('1',0) // 任何整数以0为基数取整时,都会返回本身,所以这里返回的是1
//parseInt('2',1) //注意parseInt第二个参数的取值范围为2--36,所以不满足条件,这里只能返回NaN
//parseInt('3',2) // 表示将3作为二进制来进行处理,但是二进制只有0和1,所以3超出了范围,无法转换,返回`NaN`
//parseInt('4',3) //将4作为三进制来处理,但是4无法用三进制的数据表示,返回NaN
Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性
Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组
const person = {
name: "Lydia",
age: 21
}
for (const [x, y] of Object.entries(person)) {
console.log(x, y) //name Lydia and age 21
}
//[['name','Lydia'],['age',21]]
//使用 for-of 循环,我们可以迭代数组中的每个元素,上述情况是子数组。 我们可以使用 const [x,y] 在 forof 循环中解构子数组。 x 等于子数组中的第一个元素, y 等于子数组中的第二
Vue生命周期中父子组件顺序
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
即使通讯方式
- 轮询
- 长轮询
- iframe流(长连接)
- websocket双向通信
逻辑与(&&)和逻辑或(||)
&&
当第一个条件为
真
时,执行
后面
的当第一个条件为假时,不返回任何内容
||
当第一个条件为
真
时,后面
不执行
当第一个条件为假时,执行后面的
indexOf()不支持正则表达式
不支持冒泡的事件
- mouseenter
- mouseleave
- blur
- focus
- load
- unload
- resize
“俺(unload)”“妈(mouseenter) 妈(mouseleave) 不(blur) 让(resize) 浪(load) 费(focus)”
nodeType
- 1:元素节点
- 2:属性节点
- 3:文本节点
- 8:注释节点
NOSCRIPT标签
用来定义在脚本未被执行时的替代内容
Promise
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res)); //"two"
当我们向 Promise.race 方法中传入多个 Promise 时,会进行 优先 解析。在这个例子中,我们用 setTimeout给 firstPromise 和 secondPromise 分别设定了500ms和100ms的定时器。这意味着 secondPromise 会首先解析出字符串 two 。那么此时 res 参数即为 two ,是为输出结果
Promise.reject(0).catch(e => e).catch(e => console.log(e))
错误处理是只执行最近的一个catch的。所以只有第一个catch被执行了。
但是其实catch也是可以写成链的。那就是其中一个catch没能处理掉error,则抛出这个错误给下一个catch
const promise1 = Promise.resolve('First')
const promise2 = Promise.resolve('Second')
const promise3 = Promise.reject('Third')
const promise4 = Promise.resolve('Fourth')
const runPromises = async () => {
const res1 = await Promise.all([promise1, promise2])
const res2 = await Promise.all([promise3, promise4])
return [res1, res2]
}
runPromises()
.then(res => console.log(res))
.catch(err => console.log(err))
//'Third'
Promise.all 方法可以并行式运行promise。如果其中一个promise失败了, Promise.all 方法会带上被reject 的promise的值rejects
。在这个例子中, promise3 带着 “Third” 值reject。我们在调用 runPromises 时在 runPromises 函数内部的 catch 方法去捕获任意error从而捕获到被reject的值。因为 promise3 带着 “Third” 被reject,所以只有 “Third” 打印。
构造函数
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}
const myCar = new Car();
console.log(myCar.make); //"Maserati"
//返回属性的时候,属性的值等于 返回的 值,而不是构造函数中设定的值。
//我们返回了字符串 "Maserati" ,所以myCar.make 等于 "Maserati"
delete注意点
const name = "Lydia";
age = 21;
console.log(delete name); //false
console.log(delete age); //true
//delete 操作符返回一个布尔值:true 指删除成功,否则返回 false但是通过 var , const 或 let 关键字声明的变量无法用 delete 操作符来删除
//name 变量由 const 关键字声明,所以删除不成功:返回 false . 而我们设定 age 等于 21 时,我们实际上添加了一个名为 age 的属性给全局对象。对象中的属性是可以删除的,全局对象也是如此,所以 delete age 返回true
解构赋值
const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;
console.log(y) // 1
JSON.stringify
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};
const data = JSON.stringify(settings, ["level", "health"]);
console.log(data); //"{"level":19, "health":90}"
JSON.stringify 的第二个参数是 替代者(replacer). 替代者(replacer)可以是个函数或数组,用以控制哪些值如何 被转换为字符串。
如果替代者(replacer)是个 数组 ,那么就
只有包含在数组中的属性
将会被转化为字符串。在本例中,只有名 为 “level” 和 “health” 的属性被包括进来, “username” 则被排除在外。 data 就等于 “{“level”:19, “health”:90}” .而如果替代者(replacer)是个 函数,这个函数将被对象的每个属性都调用一遍。 函数返回的值会成为这个属性的 值,最终体现在转化后的JSON字符串中(译者注:Chrome下,经过实验,如果所有属性均返回同一个值的时候有 异常,会直接将返回值作为结果输出而不会输出JSON字符串),而如果返回值为 undefined ,则该属性会被排除 在外。
字符串的padStart方法
const name = "Lydia Hallie"
console.log(name.padStart(13)) // " Lydia Hallie" , "Lydia Hallie"
console.log(name.padStart(2)) //( "[1x whitespace]Lydia Hallie" , "Lydia Hallie" )
使用 padStart 方法,我们可以在字符串的开头添加填充。传递给此方法的参数是字符串的总长度(包含填充)。 字符串 Lydia Hallie 的长度为 12 , 因此 name.padStart(13) 在字符串的开头只会插入1( 13 – 12 = 1 ) 个空格。
如果传递给 padStart 方法的参数
小于
字符串的长度,则
不会
添加填充。
var a={}, b='123', c=123;
a[b]='b'; //a['123'] = 'b'
a[c]='c'; //a['123'] = 'c'
console.log(a[b]); // c
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b'; // a['Symbol('123')'] = 'b';
a[c]='c'; // a['Symbol('123')'] = 'c'
console.log(a[b]); //b
var a={}, b={key:'123'}, c={key:'456'};
a[b]='b'; // a['[object Object]'] = 'b';
a[c]='c'; // a['[object Object]'] = 'c';
console.log(a[b]); // c
symbol的值都各不相同 唯一 所以不会被覆盖
对象的键名如果不是字符串的话 会调用tostring 方法 转换为 [object type] 相同 会进行覆盖
<input type=”text” /> change 事件和input事件
- 用户键入内容改变时,触发input事件,且在当标签失焦后,触发change事件
Number.isNaN()和isNaN()
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name)); // false
console.log(Number.isNaN(age)); // false
console.log(isNaN(name)); // true
console.log(isNaN(age)); // false
//Number.isNaN ,你可以检测你传递的值是否为 数字值 并且是否等价于 NaN
//isNaN,你可以检测你传递的值是否一个 number
Class
class Bird {
constructor() {
console.log("I'm a bird")
}
}
class Flamingo extends Bird {
constructor() {
console.log("I'm pink. 🌸");
super();
}
}
const pet = new Flamingo(); // I'm pink. 🌸 I'm a bird.
我们创建了类 Flamingo 的实例 pet 。当我们实例化这个实例, Flamingo 中的 constructor 被调用。首先,输出 “I’m pink. 🌸” , 之后我们调用
super()
。 super()
调用父类的构造函数
, Bird 。 Bird 的构造函数被调用,并输出 “I’m a bird. “
class Calc {
constructor() {
this.count = 0
}
increase() {
this.count ++
}
}
const calc = new Calc()
new Calc().increase()
console.log(calc.count) //0
我们设置 calc 变量为 Calc 类的一个新实例。 然后,我们初始化一个 Calc 的新实例,而且调用了这个实例 的 increase 方法。
因为count属性是在 Calc class的constructor内部的
,所以count属性
不会
在 Calc 的原 型链上共享出去。这就意味着calc实例的count值不会被更新,count仍然是 0 。
CSS
可以被继承的属性
- 文本(font-)
- 颜色(背景颜色不可以!)
- 列表(list-style-type)
- 元素可见性visibility
宽度计算
-
ele.
client
Width = 宽度 + padding -
ele.
offset
Width = 宽度 + padding + border -
ele.
scroll
Top = 被卷去的上侧距离 -
ele.
scrollHeight
= 自身实际的高度(不包括边框)
#container {
width: 200px;
height: 200px;
padding: 20px;
margin: 20px;
border: solide 10px black;
}
BFC
HTML元素要创建BFC,则满足下列的任意一个或多个条件
-
float的值
不是
none。 -
position的值
不是
static或者relative。 - display的值是inline-block、table-cell、flex、table-caption或者inline-flex
-
overflow的值
不是
visible
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
<style type="text/css">
.a, .b, .c {
box-sizing: border-box;
border: 1px solid;
}
.wrap {
width: 250px;
}
.a {
width: 100px;
height: 100px;
float: left;
}
.b {
width: 100px;
height: 50px;
float: left;
}
.c {
width: 100px;
height: 100px;
display: inline-block;
}
</style>
<div class="wrap">
<div class="a">a</div>
<div class="b">b</div>
<div class="c">c</div>
</div>
wrap 这个 div 的高度是多少 150px
display:inline-block会形成BFC,即块级格式化上下文,它是一个独立渲染的区域,并且与外部区域毫不相干,不会与浮动元素重叠。
因此会排在b后面,但是父盒子规定了宽度,所以就会飘下来。
meta元字符
width - viewport的宽度 height - viewport的高度 initial-scale - 初始的缩放比例 minimum-scale - 允许用户缩放到的最小比例 maximum-scale - 允许用户缩放到的最大比例 user-scalable - 用户是否可以手动缩放
<a></a>标签属性
_blank 在新窗口打开链接
_self 在
当前框架
中打开链接
_parent 在父框架打开链接
_top 在
当前窗口
打开链接
framename 在指定框架打开链接
HTML中script标签,defer和async属性
defer(告诉浏览器不要等待脚本)
-
具有 defer 特性的脚本不会阻塞页面。
-
具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行
-
脚本的执行是可以保证顺序的
async(脚本是完全独立的)
-
浏览器不会因 async 脚本而阻塞(与 defer 类似)。
-
其他脚本不会等待 async 脚本加载完成,同样,async 脚本也不会等待其他脚本
-
脚本下载完成后会立即执行 并且是无序的
CSS 样式开启硬件加速
transform: translateZ(0);
typescript是JS的超集,本质是增加了类型约束,类型推导
网络协议
ipv6报文头中的字段
版本号、流标签(flow label)、跳数限制
进程、线程、协程
- 拥有自己独立的堆和栈,既不共享堆,也不共享栈
- 线程是CPU独立运行和独立调度的基本单位
- 协程是一种用户态的轻量级线程
- 一个线程可以创建另外一个线程
TCP建立连接和断开连接时,客户端和服务端的变化
-
SYN_SENT
是
客户端
发送
SYN
包之后的立即进入的状态,
不用
等待2ml -
服务端
收到客户端的
SYN
包后会进入
SYN_RCVD
状态,服务端收到
ACK
包后会进入
established
状态 - 当客户端处于ESTABLISHED状态时,服务端等待接收客户端的ACK,所以可能处于SYN_RCVD状态
- 客户端收到服务端确认包后,等待2*ml时间后会直接关闭连接。若没有收到,则不会关闭连接
交换机收到目的MAC为组播MAC的数据包将会
学习源MAC
批处理系统的主要缺点是
失去了交互性
常见状态码
-
301
永久
重定向 -
302
临时
重定向 - 304 资源未被修改(去请求服务端,服务端告诉这个资源在本地还有效,不用再请求)
- 401(未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应
- 403 Forbidden: 服务器禁止访问,原因有很多,比如法律禁止、信息敏感、没有权限。
- 404 资源未找到
- 405: 请求方法不被服务器端允许。
- 500: 仅仅告诉服务器出错了
- 501: 表示客户端请求的功能还不支持。
- 502: 服务器自身是正常的,但访问的时候出错了
- 503: 表示服务器当前很忙,暂时无法响应服务。
HTTP请求方法
加密算法
MD5
- 对称加密算法
- 生成128位的字符串
RSA
- 非对称加密算法
- 允许选择公钥的大小
正则表达式
正则 | 定义 |
\d | 匹配一个数字,等价于[0-9] |
\w | 匹配字母、数字或者下划线,等价于 [A-Za-z0-9_] |
\s | 匹配一个空白字符,包括空格、制表符、换页符和换行符 |
* | 匹配前一个表达式 0 次或多次 |
+ | 匹配前面一个表达式 1 次或者多次 |
? | 匹配前一个表达式 0 次或1次 |
以下代码的执行后,str 的值是:
var str = “Hellllo world”;
str = str.replace(/(l)\1/g, ‘$1’);(l)表示第一个分组里有l \1表示所
获取
的第1个()匹配的引用 $1表示第一个分组里的值l那么
(l)\1 表示匹配两个连续字符ll,即ll
(l)\l/g 表示全局匹配两个连续字符ll即llll
str.replace(/(l)\1/g, ‘$1’) 表示将ll替换成l
3、最终
Hellllo => Hello
数据结构
哈夫曼树计算
若以{4,5,6,7,8}作为叶子结点的权值构造哈夫曼树,则其带权路径长度是
- 正序排列,然后从中选取两个最选的作为开端
- 这里选择4,5
距离根节点距离
0 o
1 o o
2 6 7 8 o
3 4 5
W=(4+5)*3+(6+7+8)*2=69
给定一组权值w={9, 12, 6, 3, 5, 15},其构成的哈夫曼树带权路径为
0 o
1 o o
2 o 9 12 15
3 o 6
4 3 5
w = (3+5)*4 + 6*3 + (9+12+15)*2 = 32 + 18 + 72 .= 122
二叉树遍历
某二叉树的先序遍历序列为 ABDGCEFH,中序遍历序列为 DGBAECHF,则其后序遍历 序列是
先序ABDGCEFH
中序DGB|A|ECHF
个人习惯:根两边划分
先序:根左右
中序:左根右
后序:左右根
A为根,B,D,G都在A的左侧,ECHF都在A的右侧
即:
A
B C
D E F
G H
GDBEHFCA
完全二叉树最大节点数
2^k-1 (k表示深度)
已知一棵完全二叉树的节点总数为11个, 则最后一层的节点数
深度应该是4,前3层共结点数2的3次方 -1 = 7, 11 – 7 等于4,所以最后一层结点数是4
数据结构的基本类型
- 线性结构
- 树形结构
- 图形结构