Open sunnychuai opened 4 years ago
一、JS 基础知识 闭包 原型 原型链 继承 New 关键字 1、创建一个空对象,并且this变量引用该对象,同时继承了该函数的原型 2、属性和方法被加入到this引用的对象中 3、新创建的对象由this所引用,并且最后隐式的返回this 1、创建一个空对象,作为要返回的对象实例 2、将这个空对象的原型,指向构造函数的prototype属性 3、将这个空对象赋值给函数内部的this关键字 4、开始执行构造函数内部的代码,如果函数内部有return 语句,而且return 后跟着一个对象,则返回return语句指定的对象;否则,就不管return 语句,返回this对象(新创建的空对象)。
this 指向 面向对象 设计模式 数组去重 Array.from(new Set([1, 2, 3, 3])) [...new Set([1, 2, 3, 3])] 排序算法
防抖函数
ES6 新语法 let 用来声明变量 只在let命令所在的代码块内有效。
var
const cat = { lives: 9, jumps: () => { this.lives--; // this指向window对象,而不是cat } }
var button = document.getElementById('press'); button.addEventListener('click', () => { this.classList.toggle('on'); });
这时this是全局对象,点击按钮会报错。
symbol 新增原始数据类型,表示独一无二的值 JS的七种数据类型:字符串、数值、布尔值、对象、null、undefined、symbol 对象的属性名现在可以有两种类型,一种是字符串,另一种就是Symbol类型。
[...new Set([1, 2, 2, 4, 4])] 或者 Array.from(new Set([1, 2, 2, 4, 4]))
遍历方法
let set = new Set([1, 4, 9]) set.forEach((value, key) => console.log(`${key}:${value}`)) // 1: 1 // 4: 4 // 9: 9
可以使用for...of遍历Set 扩展运算符(...) 改变Set结构的两种方法
let set = new Set([1, 2, 3]) set = new Set([...set].map(val => val * 2))
let set = new Set([1, 2, 3]) set = Array.from(set, val => val * 2)
Map结构:类似于对象,但是键不限于字符串,各种类型的值都可以作为键。 Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。 实例的属性操作方法
const map = new Map([ [1, "one"], [2, "two"], [3, "three"], ]); [...map.keys()] // [1, 2, 3] [...map.values()] // ["one", "two", "three"] [...map] // [[1, "one"], [2, "two"], [3, "three"]]
与其他数据结构的互换
let obj = Object.create(null); for(let [k, v] of map) { obj[k] = v; }
对象->Map
let map = new Map(); for(let k of obj) { map.set(k, obj[k]) }
Proxy,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外面的访问进行过滤和改写。
var proxy = new Proxy(target, handler);
const promise = new Promise((resolve, reject) => { if (/* 异步操作成功*/) { resolve(value); } else { reject(error); } })
resolve函数,将Promise对象状态从未完成变为成功(即pending变为resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去。 reject函数,将Promise对象的状态从未完成变为失败(即pending变为rejected),在异步操作失败时调用,并将失败操作报出的错误,作为参数传递出去。
// Promise 对象实现Ajax操作 const getJson = (url) => { const promise = new Promise((resolve, reject) => { const handler = () => { if(this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)) } } const client = new XMLHttpRequest(); client.open("GET", url); client.onreadystateChange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application./json"); client.send(); }) return promise; } getJson("/post.json").then((json) => { console.log("成功了", json) }, (error) => { console.log("出错了", error) })
then方法,接受两个回调函数作为参数。第一个状态变为resolved时调用,第二个状态变为rejected时调用。返回一个新的Promise实例。
catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 处理前面三个Promise产生的错误 });
finally方法用于指定不管Promise对象最后的状态如何,都会执行的操作。与状态无关,不依赖Promise的执行结果。
all方法用于将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.all([p1, p2, p3])
p的状态由p1,p2,p3决定,分成两种情况
race方法将多个Promise实例包装成一个新的Promise实例。
const p = Promise.race(p1, p2, p3)
只要p1,p2,p3之中一个实例率先改变状态,p的状态就跟着改变。
Promise.resolve() 将现有对象转为Promise对象。
Promise.resolve("foo") // 等价于 new Promise(resolve => resolve("foo"))
setTimeout(() => { console.log(3) }) Promise.resolve().then(() => { console.log(2) }) console.log(1) // 1 // 2 // 3
Promise.reject()方法也会返回一个新的Promise实例,该实例的状态为rejected。
const p = Promise.reject("出错了") // 等价于 const p = new Promise((resolve, reject) => reject("出错了"))
Promise.reject()方法的参数,会原封不动的作为reject的理由,变成后续方法的参数
Iterator遍历器 一种接口,为各种不同的数据结构提供统一的访问机制。
async/await 是Generator函数的语法糖。 async 函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
// 函数声明 async function foo () {} // 函数表达式 cont foo = async function() {} // 对象的方法 let obj = { async foo() {} }; // class的方法 class Storage { constructor() { this.cachePromise = caches.open("123"); } async getAvatar(name) { const cache = await this.cachePromise; return cache; } } // 箭头函数 const foo = async () => {};
使用注意点
await命令只能在用async函数中,如果用在铍铜函数,就会报错。 class 定义类
function Pointer(x, y) { this.x = x this.y = y } Pointer.prototype.toString = fucntion () { return '(' + this.x + ', ' + this.y + ')'; } var p = new Pointer(1, 2);
class Pointer { contructor(x, y) { this.x = x; this.y = y } toString() { return '(' + this.x + ', ' + this.y + ')'; } }
ES6的类,完全可以看作构造函数的另一种写法。
typeof Pointer // "function" Pointer === Pointer.prototype.constructor // true
ES6的类的所有方法都定义在类的prototype属性上面。 constructor方法是类的默认方法,通过new命令生成的对象实例时,自动调用该方法。默认返回实例对象(即this),也可以指定返回另外一个对象。 一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。 注意点
this指向 类的方法内部如果包含this,它默认指向类的实例。
class Logger { printName(name = "there") => { this.print(`Hello ${name}`) } print (text) { console.log(text) } } const logger = new Logger(); const { printName } = logger; printName(); // TypeError: Cannot read property 'print' of undefined
如果方法单独使用,this会指向该方法运行时所在环境,this实际指向的是undefined 解决方案
class Logger { constructor() { this.printName = this.printName.bind(this); } // ... }
class obj { constructor() { this.getThis = () => this; } } const myObj = new obj(); myObj.getThis() === myObj // true
箭头函数内部的this总是指向定义时所在的对象。上面箭头函数位于构造函数内部,它的定义生效的时候是在构造函数执行的时候。这时,箭头函数所在的运行环境,肯定是实例对象,所有this会总是指向实例对象。
function selfish (target) { const cache = new WeakMap(); const handler = { get (target, key) { const value = Reflect.get(target, key); if (typeof value !== 'function') { return value; } if (!cache.has(value)) { cache.set(value, value.bind(target)); } return cache.get(value); } }; const proxy = new Proxy(target, handler); return proxy; }
const logger = selfish(new Logger());
静态方法 static关键字定义称为静态方法,不会被实例继承,而是直接通过类来调用。 *静态方法包含this关键字,这个this指的是类,而不是实例。* 父类的静态方法,可以被子类继承。
class Foo { static classMethod() { return "hello"; } } class Bar extends Foo {} Bar.classMethod(); // "hello"
静态方法也可以从super对象上调用。
class Foo { static classMethod() { return "hello"; } }
class Bar extends Foo { static classMethod() { return super.classMethod() + ", too"; } } Bar.classMethod(); // hello, too
实例属性 定义属性两种方式 - 定义在constructor()方法里的this上面
class IncreasingCounter { constructor() { this._count = 0; } get value() { return this._count; } } ```
class IncreasingCounter { _count = 0; get value() { return this._count; } }
静态属性 静态属性指的是class本身的属性。即Class.propName,而不是定义在实例对象(this)上的属性。
class Foo {} Foo.prop = 1
ES6规定,class内部只有静态方法,没有静态属性。
私有方法和私有属性 私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。但ES6不提供,只能通过变通方法模拟实现。
class Widget{ foo(baz) { this._bar(baz); } _bar(baz) { return this.snaf = baz; } }
class Widget{ foo(baz) { bar.call(this, baz) } } function bar(baz) { return this.snaf = baz; }
Object.getPrototypeOf() 方法可以用来从子类上获取父类。因此可以用这个方法判断,一个类是否继承了另一个类。
Object.getPrototypeOf(ColorPointer) === Pointer // true
super关键字
原生构造函数
Module ES6模块的设计思是尽量静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。 CommonJs 和 AMD 模块都只能在运行时确定。
// CommonJs 模块 let { stat, exsits, readFile } = require("fs"); // 等同于 let _fs = require("fs") let stat = _fs.stat; let exists = _fs.exists; let readFile = _fs.readFile;
上面实质是整体加载fs模块(加载fs所有方法),生成对象,然后再从这个对象上读取3个方法,这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没有办法在编译时做“静态优化”。
ES6模块不是对象,而是通过export 命令显式的指定输出的代码,在通过import命令输入。
import { stat, exists, readFile } from "fs";
上面的实质上是从fs模块加载3个方法,其他方法不加载,这种加载称为“编译时加载”或静态加载。
export命令 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。因此必须使用export关键字输出。 export命令规定的是对外的接口
// 报错 export 1; //报错 const m = 1; export m;
输出的是1,只是一个值,不是接口。
export var m = 1; const m = 1; export { m }; var n = 1; export { n as m };
实质是接口名与模块内变量之间,建立一一对应的关系。 另外,export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var foo = "bar"; setTimeout(() => foo = "baz", 500)
export命令可以出现在模块任何顶层的位置。
import * as circle from "./circle" circle.area(4); circle.circumference(14);
function add(x, y) { return x * y; } export {add as default}; // 等同于 // export default add;
import { default as foo } from "modules" // 等同于 // import foo from "modules"
- import() 动态加载,返回一个Promise对象。 使用场景: - 按需加载
button.addEventListener('click', event => { import('./dialogBox.js') .then(dialogBox => { dialogBox.open(); }) .catch(error => { /* Error handling */ }) }); ```
import('./myModule.js') .then(({export1, export2}) => { // ...· });
编写风格优化
函数
// bad const self = this; const boundMethod = function(...params) { return method.apply(self, params); }
// acceptable const boundMethod = method.bind(this);
// best const boundMethod = (...params) => method.apply(this, params);
- 所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数
// bad function divide(a, b, option = false ) { }
// good function divide(a, b, { option = false } = {}) { }
- 不要在函数体内使用arguments变量,使用rest运算符(...)代替。 - 使用默认值语法设置函数参数的默认值。
浏览器输入网址后的过程 浏览器缓存 跨域的解决方案 Ajax 实现过程 正则表达式 二、框架 Vue vuex vue-router
React Redux / Mobx React-router
React Hooks
三、小程序
四、Nodejs 、Koa
五、Webpack
六、Typescript
七、Flutter
八、React-Native
面向对象 对象是实物的抽象,是一个容器,封装了属性和方法。 如果创建实例对象?
this 是属性或方法“当前”所在的对象。 函数中的this就是函数运行时所在的对象。
改变this指向
构造函数的缺点 同一个构造函数的多个实例之间,无法共享属性,造成对系统资源的浪费。 解决方案就是原型对象。
继承机制的设计思想就是 原型对象的所有属性和方法都能被实例对象共享。 原型对象的属性不是实例对象自身的属性,只要修改原型对象,变动就会体现在所有实例对象上。 实例对象就有某个属性或方法,不会再去原型对象寻找这个属性和方法。 原型对象的作用就是定义所有实例对象共享的属性和方法。
如何理解原型和原型链? 所有的对象都有自己的原型对象,一方面,任何对象都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此就会形成一个“原型链”。 一层层追溯所有对象的原型最终都可以上溯到Object.prototype。也就是所有对象都继承了Object.prototype的属性,比如valueOf 和 toString方法。 而Object.prototype的原型是null。null 没有任何属性和方法,也没有自己的原型,因此,原型链的尽头就是 null。 读取对象的属性时,JS引擎先寻找对象本身的属性,找不到就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型都定义一个同名属性,那么优先读取对象自身的属性。
constructor属性 prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。 constructor属性的作用
instanceof 运算符 返回一个布尔值,表示对象是否为某个构造函数的实例。 原理:检查右边的构造函数的prototype属性,是否在左边对象的原型链上。 特殊情况,左边对象的原型链上,只有null对象时,instanceof 判断会失真。
一、JS 基础知识 闭包 原型 原型链 继承 New 关键字 1、创建一个空对象,并且this变量引用该对象,同时继承了该函数的原型 2、属性和方法被加入到this引用的对象中 3、新创建的对象由this所引用,并且最后隐式的返回this 1、创建一个空对象,作为要返回的对象实例 2、将这个空对象的原型,指向构造函数的prototype属性 3、将这个空对象赋值给函数内部的this关键字 4、开始执行构造函数内部的代码,如果函数内部有return 语句,而且return 后跟着一个对象,则返回return语句指定的对象;否则,就不管return 语句,返回this对象(新创建的空对象)。
this 指向 面向对象 设计模式 数组去重 Array.from(new Set([1, 2, 3, 3])) [...new Set([1, 2, 3, 3])] 排序算法
防抖函数
ES6 新语法 let 用来声明变量 只在let命令所在的代码块内有效。
var
,即会提升到全局作用域或函数作用域的头部这时this是全局对象,点击按钮会报错。
symbol 新增原始数据类型,表示独一无二的值 JS的七种数据类型:字符串、数值、布尔值、对象、null、undefined、symbol 对象的属性名现在可以有两种类型,一种是字符串,另一种就是Symbol类型。
遍历方法
可以使用for...of遍历Set 扩展运算符(...) 改变Set结构的两种方法
Map结构:类似于对象,但是键不限于字符串,各种类型的值都可以作为键。 Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。 实例的属性操作方法
与其他数据结构的互换
对象->Map
Proxy,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外面的访问进行过滤和改写。
resolve函数,将Promise对象状态从未完成变为成功(即pending变为resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去。 reject函数,将Promise对象的状态从未完成变为失败(即pending变为rejected),在异步操作失败时调用,并将失败操作报出的错误,作为参数传递出去。
then方法,接受两个回调函数作为参数。第一个状态变为resolved时调用,第二个状态变为rejected时调用。返回一个新的Promise实例。
catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
finally方法用于指定不管Promise对象最后的状态如何,都会执行的操作。与状态无关,不依赖Promise的执行结果。
all方法用于将多个Promise实例,包装成一个新的Promise实例。
p的状态由p1,p2,p3决定,分成两种情况
race方法将多个Promise实例包装成一个新的Promise实例。
只要p1,p2,p3之中一个实例率先改变状态,p的状态就跟着改变。
Promise.resolve() 将现有对象转为Promise对象。
Promise.reject()方法也会返回一个新的Promise实例,该实例的状态为rejected。
Promise.reject()方法的参数,会原封不动的作为reject的理由,变成后续方法的参数
Iterator遍历器 一种接口,为各种不同的数据结构提供统一的访问机制。
async/await 是Generator函数的语法糖。 async 函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
使用注意点
await命令只能在用async函数中,如果用在铍铜函数,就会报错。 class 定义类
ES6的类,完全可以看作构造函数的另一种写法。
ES6的类的所有方法都定义在类的prototype属性上面。 constructor方法是类的默认方法,通过new命令生成的对象实例时,自动调用该方法。默认返回实例对象(即this),也可以指定返回另外一个对象。 一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。 注意点
this指向 类的方法内部如果包含this,它默认指向类的实例。
如果方法单独使用,this会指向该方法运行时所在环境,this实际指向的是undefined 解决方案
箭头函数内部的this总是指向定义时所在的对象。上面箭头函数位于构造函数内部,它的定义生效的时候是在构造函数执行的时候。这时,箭头函数所在的运行环境,肯定是实例对象,所有this会总是指向实例对象。
const logger = selfish(new Logger());
class Foo { static classMethod() { return "hello"; } } class Bar extends Foo {} Bar.classMethod(); // "hello"
class Foo { static classMethod() { return "hello"; } }
class Bar extends Foo { static classMethod() { return super.classMethod() + ", too"; } } Bar.classMethod(); // hello, too
静态属性 静态属性指的是class本身的属性。即Class.propName,而不是定义在实例对象(this)上的属性。
ES6规定,class内部只有静态方法,没有静态属性。
私有方法和私有属性 私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。但ES6不提供,只能通过变通方法模拟实现。
Object.getPrototypeOf() 方法可以用来从子类上获取父类。因此可以用这个方法判断,一个类是否继承了另一个类。
super关键字
原生构造函数
Module ES6模块的设计思是尽量静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。 CommonJs 和 AMD 模块都只能在运行时确定。
上面实质是整体加载fs模块(加载fs所有方法),生成对象,然后再从这个对象上读取3个方法,这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没有办法在编译时做“静态优化”。
ES6模块不是对象,而是通过export 命令显式的指定输出的代码,在通过import命令输入。
上面的实质上是从fs模块加载3个方法,其他方法不加载,这种加载称为“编译时加载”或静态加载。
export命令 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。因此必须使用export关键字输出。 export命令规定的是对外的接口
输出的是1,只是一个值,不是接口。
实质是接口名与模块内变量之间,建立一一对应的关系。 另外,export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export命令可以出现在模块任何顶层的位置。
import { default as foo } from "modules" // 等同于 // import foo from "modules"
编写风格优化
函数
// acceptable const boundMethod = method.bind(this);
// best const boundMethod = (...params) => method.apply(this, params);
// bad function divide(a, b, option = false ) { }
// good function divide(a, b, { option = false } = {}) { }
浏览器输入网址后的过程 浏览器缓存 跨域的解决方案 Ajax 实现过程 正则表达式 二、框架 Vue vuex vue-router
React Redux / Mobx React-router
React Hooks
三、小程序
四、Nodejs 、Koa
五、Webpack
六、Typescript
七、Flutter
八、React-Native
面向对象 对象是实物的抽象,是一个容器,封装了属性和方法。 如果创建实例对象?
this 是属性或方法“当前”所在的对象。 函数中的this就是函数运行时所在的对象。
改变this指向
构造函数的缺点 同一个构造函数的多个实例之间,无法共享属性,造成对系统资源的浪费。 解决方案就是原型对象。
继承机制的设计思想就是 原型对象的所有属性和方法都能被实例对象共享。 原型对象的属性不是实例对象自身的属性,只要修改原型对象,变动就会体现在所有实例对象上。 实例对象就有某个属性或方法,不会再去原型对象寻找这个属性和方法。 原型对象的作用就是定义所有实例对象共享的属性和方法。
如何理解原型和原型链? 所有的对象都有自己的原型对象,一方面,任何对象都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此就会形成一个“原型链”。 一层层追溯所有对象的原型最终都可以上溯到Object.prototype。也就是所有对象都继承了Object.prototype的属性,比如valueOf 和 toString方法。 而Object.prototype的原型是null。null 没有任何属性和方法,也没有自己的原型,因此,原型链的尽头就是 null。 读取对象的属性时,JS引擎先寻找对象本身的属性,找不到就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。如果对象自身和它的原型都定义一个同名属性,那么优先读取对象自身的属性。
constructor属性 prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。 constructor属性的作用
instanceof 运算符 返回一个布尔值,表示对象是否为某个构造函数的实例。 原理:检查右边的构造函数的prototype属性,是否在左边对象的原型链上。 特殊情况,左边对象的原型链上,只有null对象时,instanceof 判断会失真。