技术 js 漫长的从零到一的JS调试之路 Kitholt Frank 2021-11-21 2025-11-30 漫长的从零到一的JS调试之路 关于变量 var 声明的作用域是函数作用域
使用var定义的变量会成为包含它的函数的局部变量
使用var定义的变量会自动提升到函数作用域顶部(声明提升)
let let声明的是块作用域 ,需要注意的是块作用域 是函数作用域 的子集
let定义的变量不会进行声明提升,这称为“暂时性死区”
const 可以理解为java中的声明一个常量
关于数据类型 Undefined(定义了但未赋予初始值) 它的值就是undefined,使用var或let声明变量没赋初值,就相当于赋予了undefined
Null 它的值为null,逻辑上它的值表示一个空对象指针
Boolean Number 有个特殊的值:NaN(不是数值“Not a Number”),用来表示本来要返回数值的操作失败了
String 可以用单(‘’)、双引号(“”)或者反引号(`)标示
Symbol 确保对象属性使用唯一标识符,防止属性冲突的危险
Object 关于正则表达式 用于定义一些字符串的规则
语法:
var 变量 = new RegExp(“正则表达式”,”匹配模式”);
完成上述new后得到一个正则表达式对象,随后我们就能用这个对象去检查其他字符串是否符合该正则表达式的规范
此外还能使用字面量来创建正则表达式:
var 变量 = /正则表达式/匹配模式
正则表达式规则:
/[abc]/:表示含有a或b或c的字符串
/[ ^ abc]/:表示匹配除了[abc]的所有字符
/a{n}/:花括号的n表示对它前面一个内容起作用,{n}表示正好出现n次
扩展:/(abcd){n}/:表示abcd这一个整体正好出现n次
另外也可以这样写,/a{x,y}/,这表示a可以出现x到y次(出现次数”区间化“)
具体规则还有好多,可参考官方文档…
关于DOM
全称Document Object Model
js通过DOM来对html文档进行操作。
Document:指的是整个html页面
Object:将网页的每一部分都转换为一个对象(每一个标签)
Model:使用模型表示对象之间的关系,方便我们获取对象
onload事件会在整个页面加载完成之后才触发
为window绑定一个onload事件,该事件对应的响应函数将会在页面加载完成之后执行,这样就可以确保我们的代码执行时所有的DOM对象已经加载完毕
关于BOM
事件句柄
关于typeof、instanceof、===(这个不算高级吧)
关于区分数据类型和变量类型
乍一看很像,说简单点,变量类型就是一个装数据类型的容器,比如变量类型中的基本类型保存的就是基本类型的数据,例子有var a = 3
立刻执行函数
类似于函数声明,但由于包含在括号内,所以会被解释成函数式表达式,紧跟在第一组括号后面的第二组括号会立即调用前面的函数表达式,具体如下所示:
集合引用类型 Array 创建数组 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 <script> let colors = new Array (20 ) console .log (colors) let color = ['red' ,'green' ,'blue' ] console .log (color) const strArr = Array .from ('kitholt' ) console .log (strArr) const m = new Map ().set (1 ,2 ).set (3 ,4 ) console .log (Array .from (m)) const s = new Set ().add (1 ).add (2 ).add (3 ) console .log (Array .from (s)) const a1 = [1 ,[6 ,66 ],3 ,4 ] const a2 = Array .from (a1) a1[1 ][0 ] = 99 console .log (a2 === a1); console .log (a2[1 ][0 ]) </script>
数组复制和填充 1 2 3 4 5 6 7 8 9 10 11 <script> const zeros = [0 ,0 ,0 ,0 ,0 ] zeros.fill (1 ,1 ,4 ) console .log (zeros) const ints = [0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ] ints.copyWithin (4 ,0 ,3 ) console .log (ints) </script>
数组排序方法 1 2 3 4 5 6 7 8 <script> const val = [1 ,4 ,2 ,5 ,41 ,32 ,7 ,6 ] val.sort ((a, b ) => a > b ? 1 : a < b ? -1 : 0 ) console .log (val) console .log (val.reverse ()) </script>
数组的操作方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script> const arr = ['fran' ,'伟鸿' ,'陈部' ,'志坚' ] const arrConcat = arr.concat ('胖子' ,'仕豪' ,['浩彬' ,'陈尹' ]) console .log (arrConcat) const arrSlice = arrConcat.slice (2 ) console .log (arrSlice) arr.splice (0 ,2 ) console .log ('***' ,arr) arr.splice (1 ,0 ,'胖子' ,'仕豪' ) console .log (arr) </script>
数组的迭代器方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script> const colors = ['red' ,'green' ,'blue' ] const colorsIndex = Array .from (colors.keys ()) console .log (colorsIndex) for (let i of colors.values ()){ console .log (i) } const colorVal = Array .from (colors.values ()) console .log (colorVal) const colorEntries = Array .from (colors.entries ()) console .log (colorEntries) </script>
数组索引 1 2 3 4 5 6 7 8 9 10 11 12 13 <script> const colors = ['red' ,'green' ,'blue' ] console .log ('原数组' ,colors) colors[colors.length ] = 'black' console .log ('插入black' ,colors) colors.length = 2 console .log ('将数组长度变为2' ,colors) </script>
Map 基本API 1 2 3 4 5 6 7 8 9 10 11 12 13 <script> const myMap = new Map () myMap.set ('k1' ,'v1' ) .set ('k2' ,'v2' ) .set ('k3' ,'v3' ) console .log (myMap) console .log (myMap.get ('k1' )) console .log (myMap.has ('k2' )) myMap.delete ('k2' ) console .log (myMap) </script>
注意点 1 2 3 4 5 6 7 8 9 10 11 12 13 <script> const m1 = new Map ([['k1' ,'v1' ]]) for (let key of m1.keys ()){ key = 'newKey' console .log (key) console .log (m1.get ('k1' )) } </script>
Set 基本API 1 2 3 4 5 6 7 8 <script> const mySet = new Set (); mySet.add ('v1' ).add ('v2' ).add ('v3' ).add ('v3' ) console .log (mySet) const result = mySet.has ('v1' ) console .log (result) </script>
对象,类与面向对象编程 对象 合并对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script> let src, dest, result dest = {} src = {id : 'src' } result = Object .assign (dest, src) console .log ("目标对象dest和增强后的对象result是否相等(同一个指针指向)?" ,result === dest) result.id = "null" console .log (result.id ) console .log (dest.id ) </script>
增强的对象语法 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 <script> const name = 'fran_name' const age = 'fran_age' const major = 'fran_major' const say = 'fran_say' let person = { [name]: 'fran' , [age]: 21 , [major]: 'Software engineer' , [say](){ console .log (`my name is ${this .fran_name} ` ) } } console .log (person) person.fran_say () </script>
对象属性的类型 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 33 34 35 36 37 38 39 40 41 42 43 <script> let person = {} person.name = 'jack' console .log (person.name ) Object .defineProperty (person,'age' ,{ writable : false , configurable : false , value : 18 }) console .log (person.age ) person.age = 21 console .log (person.age ) delete person.age let book = { year_ : 2021 , edition : 1 , } Object .defineProperty (book,'year' ,{ get ( ){ return this .year_ }, set (newVal ){ if (newVal > 2021 ){ this .year_ = newVal this .edition = newVal - 2021 } } }) book.year = 2025 console .log (book.edition ) </script>
对象解构 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 <script> let person = { name : 'fran' , age : 21 , shoes :{ nike : 'nike' , anta : 'anta' , } } let name = person.name let age = person.age console .log ('常规赋值' ) console .log (name,age) let {name : myName, age : myAge} = person console .log ('解构赋值' ) console .log (myName, myAge) let showShoes = function ({shoes:{nike, anta}} ){ console .log (nike, anta) } showShoes (person) </script>
构造函数 构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script> function Person (name, age ) { this .name = name this .age = age this .sayName = function ( ){ console .log (`halo, my name is ${this .name} . I am ${this .age} .` ) } } let fran = new Person ('fran' ,21 ) fran.sayName () </script>
继承(原型) 原型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script> function Person (name, age ) { this .name = name this .age = age Person .prototype .sayName = function ( ){ console .log (`halo, my name is ${this .name} . I am ${this .age} .` ) } } let jack = new Person ('jack' ,19 ) let fran = new Person ('fran' ,21 ) jack.sayName () fran.sayName () </script>
组合继承 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 <script> function Person (name ){ this .name = name this .nationality = [] } Person .prototype .sayName = function ( ){ console .log (`my name is ${this .name} ` ) } function YellowGuy (name, age ){ Person .call (this ,name) this .age = age } YellowGuy .prototype = new Person () YellowGuy .prototype .sayAge = function ( ){ console .log (`i am ${this .age} ` ) } let fran = new YellowGuy ('fran' ,21 ) fran.sayName () fran.sayAge () fran.nationality .push ('China' ) console .log (fran.nationality [0 ]) </script>
原型式继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <script> function object (o ){ function NewObj ( ) {} NewObj .prototype = o return new NewObj () } let person = { name : '' } let fran = object (person) fran.name = 'fran' console .log (fran.name ) </script>
寄生式组合继承 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 33 34 35 36 <script> function object (o ){ function NewObj ( ) {} NewObj .prototype = o return new NewObj () } let Person = function (name ){ this .name = name } let ChinesePerson = function (name, age ){ Person .call (this , name) this .age = age } function inheritPrototype (subType, superType ){ let prototype = object (superType.prototype ) subType.prototype = prototype } inheritPrototype (ChinesePerson , Person ) let fran = new ChinesePerson ('fran' ,21 ) console .log (fran) </script>
类 类构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <script> class Person { constructor (name ){ this .name = name this .sayName = function ( ){ console .log (`my name is ${this .name} ` ) } } } let p1 = new Person ('fran' ) p1.sayName () let p3 = new Person .constructor ( ) console .log (p3 instanceof Person .constructor ) </script>
抽象基类 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <script> class Vehicle { constructor (price ){ this .price = price if (new .target === Vehicle ){ throw new Error ('不好意思,Vehicle是一个抽象基类,无法被实例化- -,请实例化它的子类,不用谢' ) } } showPrice ( ){ console .log (`这辆车的价格是${this .price} w` ) } } Vehicle .prototype .foo = function ( ){ console .log ('foo' ) } class Car extends Vehicle { constructor (brand,price ){ super (price) this .brand = brand this .showBrand = function ( ){ console .log (`这辆车是${this .brand} ` ) } } } let toyota = new Car ('丰田' ,20 ) let auto = new Car ('大众' ,10 ) toyota.showBrand () toyota.foo () console .log (toyota.brand === auto.brand ) </script>
原型方法与访问器 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <script> class Person { constructor (name,age ){ this .name = name this .age_ = age this .locate = () => { console .log ('instance' ) } } set age (age ){ console .log ('修改age' ) this .age_ = age } get age (){ console .log ('读取age' ) return this .age_ } locate ( ){ console .log ('prototype' ) } } let p1 = new Person ('fran' ,21 ) console .log (p1.name ) p1.locate () Person .prototype .locate () p1.age p1.age = 22 </script>
不是小结的小结
顶不住了,别人画的是思维导图,我画的是些什么玩意er,可能我的思维比较混乱吧- -至少它在我脑海是这个布局
DOOOOOM 节点关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <script> let html = document .documentElement let htmlChildren = html.childNodes console .log (htmlChildren) let head = html.firstChild console .log (head) let body = html.lastChild console .log (body) let headChildren = htmlChildren[0 ].childNodes console .log (headChildren) </script>
操作节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let ul = document .documentElement .lastChild .childNodes [1 ] console .log (ul) let li = document .createElement ('li' ) li.innerHTML = 5 ul.appendChild (li) ul.removeChild (li) </script>
定位元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let htmlNodeList = document .documentElement .childNodes console .log (htmlNodeList) let htmlHTMLCollection = document .documentElement .children console .log (htmlHTMLCollection) </script>
MutationObserver 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <script> let observer = new MutationObserver (() => console .log ('<body> 标签的属性被改变了!!!' )) observer.observe (document .body , {attributes : true }) document .body .className = 'foo' console .log ('改变body的css类名' ) </script>
Selectors_API 1 2 3 4 5 6 7 8 9 10 11 12 <script> console .log (document .body .querySelectorAll ('p' )) console .log (document .body .matches ('body.p' )) </script>
CSS类扩展 1 2 3 4 5 6 7 8 9 10 11 <script> let div = document .querySelector ('.red' ) div.classList .remove ('red' ) div.classList .add ('green' ) console .log (div.style ) </script>
插入标记 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script> </script>
遍历 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 <script> let filter = { acceptNode (node ){ return node.tagName .toLowerCase () == 'p' ? NodeFilter .FILTER_ACCEPT : NodeFilter .FILTER_REJECT } } let div = document .querySelector ('.div1' ) let iterator = document .createNodeIterator (div, NodeFilter .SHOW_ELEMENT , filter, false ); let node = iterator.nextNode () while (node != null ){ console .log (node.tagName ) node = iterator.nextNode () } </script>
待续… 函数 没有重载、有默认参数值 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 33 <script> function addNum (num ){ return num + 100 } function addNum (num ){ return num + 200 } let result = addNum (30 ) console .log (result) function giveName (name = 'foo' ){ return `halo, my name is ${name} ` } let sentence = giveName () let sentence2 = giveName ('kitholt' ) console .log (sentence) console .log (sentence2) function makeKing (name = 'jack' , numerals = defaultNumeral ){ let defaultNumeral = '2' return `King ${name} ${numerals} ` } console .log (makeKing (undefined ,2 )) console .log (makeKing ()) </script>
函数声明与函数表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script> console .log (sayName ()) function sayName ( ){ return 'my name is kitholt' } console .log (sayAge ()) let sayAge = function ( ){ return '21' } </script>
扩展运算符 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 <script> function getSum ( ){ let num = 0 ; for (let i = 0 ; i < arguments .length ; i++){ num += arguments [i] } return num } let values = [1 , 2 , 3 , 4 ] console .log (getSum (...values)) function ignoreFirst (firstValue, ...values ){ console .log (values) } ignoreFirst () ignoreFirst (233 ) ignoreFirst (1 ,2 ,3 ) </script>
函数的this对象 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 <script> window .id = 'Window' let obj = { id : 'obj' , getId ( ){ return function ( ){ return this .id } } } console .log (obj.getId ()()) let obj1 = { id : 'obj1' , getId ( ){ let that = this return function ( ){ return that.id } } } console .log (obj1.getId ()()) </script>
私有变量 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 <script> function MyObj ( ){ let privateVal = 10 function privateFun ( ){ return false } this .publicMethod = function ( ){ privateVal++ return privateFun () } } let myObj = new MyObj () console .log (myObj.privateVal ) myObj.privateFun () </script>
模块模式 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 33 34 35 36 <script> function Component (id ){ this .id = id } let app = function ( ){ let components = new Array () return { getComponentCount ( ){ return components.length }, registerComponent (component ){ if (typeof component == 'object' ){ components.push (component) } } } }() console .log (app) let c1 = new Component ('1' ) let c2 = new Component ('2' ) let c3 = new Component ('3' ) app.registerComponent (c1) app.registerComponent (c2) app.registerComponent (c3) console .log (app) console .log (app.getComponentCount ()) </script>
结?(不完整)
事件 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 33 <script> function addNum (num ){ return num + 100 } function addNum (num ){ return num + 200 } let result = addNum (30 ) console .log (result) function giveName (name = 'foo' ){ return `halo, my name is ${name} ` } let sentence = giveName () let sentence2 = giveName ('kitholt' ) console .log (sentence) console .log (sentence2) function makeKing (name = 'jack' , numerals = defaultNumeral ){ let defaultNumeral = '2' return `King ${name} ${numerals} ` } console .log (makeKing (undefined ,2 )) console .log (makeKing ()) </script>
函数声明与函数表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script> console .log (sayName ()) function sayName ( ){ return 'my name is kitholt' } console .log (sayAge ()) let sayAge = function ( ){ return '21' } </script>
动画与Canvas图形 requestAnimationFrame (下面介绍摘自阮一峰老师的博客)
设置这个API的目的是为了让各种网页动画效果(DOM动画、Canvas动画、SVG动画、WebGL动画)能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。代码中使用这个API,就是告诉浏览器希望执行一个动画,让浏览器在下一个动画帧安排一次网页重绘。
**requestAnimationFrame的优势,在于充分利用显示器的刷新机制,比较节省系统资源。**显示器有固定的刷新频率(60Hz或75Hz),也就是说,每秒最多只能重绘60次或75次,requestAnimationFrame的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。此外,使用这个API,一旦页面不处于浏览器的当前标签,就会自动停止刷新。这就节省了CPU、GPU和电力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script> var a = 0 ; function step ( ){ a++; console .log (a) var g = requestAnimationFrame (step) if (a>=100 ){ cancelAnimationFrame (g) } } step () </script>
该方法也会返回一个请求ID,可以用于通过另外一个方法cancelAnimationFrame()来取消任务,这有点像setTimeout()
补充要点:什么是回流和重绘?
回流:页面中元素尺寸、布局或者隐藏而需要重新构建页面
以下操作会引起回流
添加或者删除可见的DOM元素;
元素尺寸改变——边距、填充、边框、宽度和高度
内容变化,比如用户在input框中输入文字
浏览器窗口尺寸改变——resize事件发生时
计算 offsetWidth 和 offsetHeight 属性
重绘:元素的外观发生改变,但是布局没有发生改变
在页面设计中药尽量减少回流和重绘的次数,这就是所谓的性能优化(高阶操作)
改变样式尽量集中改变,可以使用class属性给多个元素同时添加
position属性为absolute或fixed的元素,重排开销比较小,不用考虑它对其他元素的影响
优化动画- -
…
绘制矩形 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 33 34 35 36 37 38 39 40 <canvas class ="drawing" width="800" height="300" style="background-color: honeydew;" ></canvas> <script > let drawing = document .querySelector ('.drawing' ) let context = drawing.getContext ('2d' ) context.fillStyle = 'red' context.fillRect (10 , 10 , 50 , 50 ) context.strokeStyle = 'black' context.lineWidth = 10 context.strokeRect (10 , 10 , 50 , 50 ) context.fillStyle = 'rgba(0, 0, 255, 0.5)' context.fillRect (30 , 30 , 50 , 50 ) context.clearRect (40 , 40 , 10 , 10 ) </script >
绘制路径 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 33 34 35 36 37 38 39 40 41 42 43 44 <script> let drawing = document .querySelector ('.drawing' ) let context = drawing.getContext ('2d' ) context.beginPath () context.translate (400 , 400 ) context.arc (0 , 0 , 400 , 0 , 2 * Math .PI , false ) context.arc (0 , 0 , 390 , 0 , 2 * Math .PI , false ) context.rotate (1 ) context.moveTo (0 , 0 ) context.lineTo (0 , -360 ) context.moveTo (0 , 0 ) context.lineTo (-330 , 0 ) context.closePath () context.stroke () context.font = 'bold 14px Arial' context.textAlign = 'center' context.textBaseline = 'middle' context.fillText ('12' , 0 , -375 ) </script>
阴影 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script> let drawing = document .querySelector ('.drawing' ) let context = drawing.getContext ('2d' ) context.shadowOffsetX = 5 context.shadowOffsetY = 5 context.shadowColor = 'green' context.fillStyle = 'red' context.fillRect (10 , 10 , 50 , 50 ) </script>
渐变 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 33 34 35 <script> let drawing = document .querySelector ('.drawing' ) let context = drawing.getContext ('2d' ) let gradient = context.createLinearGradient (0 , 0 , 800 , 300 ) gradient.addColorStop (0 ,'red' ) gradient.addColorStop (0.5 ,'yellow' ) gradient.addColorStop (0.6 ,'pink' ) gradient.addColorStop (1 ,'blue' ) context.fillStyle = gradient context.fillRect (0 , 0 , 400 , 300 ) let radialGradient = context.createRadialGradient (600 , 150 , 90 , 490 , 150 , 200 ) radialGradient.addColorStop (0 ,'white' ) radialGradient.addColorStop (0.5 ,'purple' ) radialGradient.addColorStop (1 ,'black' ) context.fillStyle = radialGradient context.fillRect (400 , 0 , 400 , 300 ) </script>
图案 1 2 3 4 5 6 7 8 <script> </script>
图像数据 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 <canvas class ="drawing" width="2000" height="2000" ></canvas> <img src ="img/img1.jpg" alt ="" width ="500" height ="300" id ="img1" > <script > let drawing = document .querySelector ('.drawing' ) let ctx = drawing.getContext ('2d' ) let image, imageData, red, green, blue, len, average image = document .getElementById ('img1' ) window .onload = function ( ){ ctx.drawImage (image, 0 , 0 , 500 , 300 ) imageData = ctx.getImageData (0 , 0 , image.width , image.height ) data = imageData.data len = data.length for (let i = 0 ; i < len; i += 4 ){ red = data[i] green = data[i+1 ] blue = data[i+2 ] average = Math .floor ((red + green + blue) / 3 ) data[i] = average data[i+1 ] = average data[i+2 ] = average } imageData.data = data ctx.putImageData (imageData, 0 , 0 ) } </script >
合成 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 <script> let drawing = document .querySelector ('.drawing' ) let ctx = drawing.getContext ('2d' ) ctx.fillStyle = 'red' ctx.fillRect (0 , 0 , 80 , 80 ) ctx.globalAlpha = 0.5 ctx.fillStyle = 'blue' ctx.fillRect (60 , 60 , 60 , 60 ) ctx.globalAlpha = 1 ctx.fillStyle = 'red' ctx.fillRect (500 , 500 , 80 , 80 ) ctx.globalCompositeOperation = 'destination-over' ctx.fillStyle = 'blue' ctx.fillRect (560 , 560 , 60 , 60 ) </script>
小demo 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 <script> let drawing = document .querySelector ('.drawing' ) let ctx = drawing.getContext ('2d' ) drawing.width = window .innerWidth drawing.height = window .innerHeight let ballArr = [] let P = Math .PI function Ball (x, y, r ){ this .positionX = x this .positionY = y this .dx = parseInt (Math .random ()*8 -4 ) this .dy = parseInt (Math .random ()*8 -4 ) this .radius = r this .color = this .getRandomColor () ballArr.push (this ) } Ball .prototype .getRandomColor = function ( ){ let color = '#' for (let i = 0 ; i < 6 ; i++){ let randomColorNum = Math .floor (Math .random () * 9 ) color += randomColorNum } return color } Ball .prototype .updatePosition = function ( ){ this .positionX += this .dx this .positionY += this .dy this .radius -= 0.3 if (this .radius < 0 ){ this .removeBall () } } Ball .prototype .removeBall = function ( ){ for (let i = 0 ; i < ballArr.length ; i++){ if (ballArr[i] === this ){ ballArr.splice (i,1 ) } } } Ball .prototype .renderBall = function ( ){ ctx.beginPath () ctx.fillStyle = this .color ctx.arc (this .positionX , this .positionY , this .radius , 0 , 2 * P, false ) ctx.fillRect (this .positionX + 10 , this .positionY + 10 , this .radius , this .radius ) ctx.fill () } drawing.addEventListener ('mousemove' , function (event ){ new Ball (event.offsetX , event.offsetY , 15 ) }) drawing.addEventListener ('click' , function (event ){ for (let i = 0 ; i < 15 ; i++){ new Ball (event.offsetX , event.offsetY , 15 ) } }) function animation ( ){ ctx.clearRect (0 , 0 , drawing.width , drawing.height ) for (let i = 0 ; i < ballArr.length ; i++){ ballArr[i].updatePosition () if (ballArr[i]){ ballArr[i].renderBall () } } requestAnimationFrame (animation) } animation () </script>
ES67891011(特性 解构赋值 允许按照一定模式从数组和对象中提取值,对变量进行赋值
1 2 3 4 5 6 7 8 9 10 11 const group = ['张三' ,'李四' ,'王五' ,‘赵六];let [zhang, li, wang, zhao] = groupconst pepople = { name : 'Lee' , age : 18 ; say : function ( ){ console .log ('hello, my name is Lee' ); } }; let {say} = people;
模板字符串 (`)反引号
可直接出现换行符
在模板字符串里,变量使用${变量名}进行拼接
简化对象写法 允许在大括号里面直接写入变量和函数,作为对象的属性和方法
箭头函数
箭头函数的this是静态的,始终指向函数声明所在的作用域下的this的值,并不会随着调用者的不用而不同
不能作为构造函数实例化对象
不能使用argument
当只有一个形参时,可以省略小括号
此外,当代码只有一句时,可以省略return
扩展运算符(…) 能将数组转换为逗号分隔的参数序列
数组合并
数组克隆
将伪数组转为真数组
Symbol(符号) 符号实例是唯一的,用于标识对象的唯一属性
Symbol不能new,只能使用Symbol()初始化
Symbol没有字面量语法
注意区分let a = Symbol()和let a = Symbol.for();
全局注册表中定义的符号和使用Symbol()定义的符号并不相同
迭代器(Iterator) 通俗说就是遍历,可迭代对象通过实现iterable接口,而且可以通过迭代器Iterator来消费
工作原理:每个可迭代对象都有一个Symbol.Iterator的属性,这个属性是一个方法
它会创建一个指针对象,指向当前数据结构的起始位置
第一次调用对象的next方法,指针会后移指向数据结构的第一个成员
不断调用next,指针不断后移,直到到头
每调用next方法返回一个包含value和done的属性对象
生成器(generator) 1 2 3 4 5 6 function * generatorFn ( ){ yield param1; yield param2 }
function*表示这是一个生成器函数
值得一提的是,调用这个函数并不会立即执行,它会返回一个生成器对象
1 2 3 4 5 6 7 8 9 function * generatorFn ( ){ yield param1; yield param2 } let gen = generatorFn ();
yield的作用 yield有产出、产量、返回的意思,因此不难理解yield就相当于生成器里的return关键字。也就是说生成器函数能够返回多次(执行多次)
next() 它实现了iterator接口,因此也有next()方法,调用该方法能执行每个yield之前的代码,并且next()也可传入参数,该参数会覆盖上一个yield返回的值
promise(一个对象) 用于封装异步操作
promise的出现是为了解决回调地狱
Promise是一个构造函数,通过new关键字实例化对象
语法
1 new Promise ((resolve, reject ) => {});
Promise接受一个函数作为参数(参数函数)
在参数函数中接受两个参数:
promise实例
state:状态
pending(准备)
fulfilled(成功)
rejected(失败)
通过调用resolve()和reject()来改变promise对象的状态(分别对应fulfilled和rejected)
result:结果
promise状态的改变是一次性的
promise的结果 1 2 3 4 5 6 7 8 9 10 11 12 const p = new Promise ((resolve,reject )=> { setTimeout (() => { resolve ('用户数据' ); }, 1000 ); }); p.then ((value ) => { console .log (value+'访问成功' ); }).catch (()=> { console .log ('访问失败' ); });
promise的方法
1 2 3 4 5 6 7 8 9 <script> const p = new Promise ((resolve,reject ) => { resolve ('yes' ); }); p.then (() => {console .log ('成功调用' )}, () => {console .log ('调用失败...' )}); console .log (p); </script>
注意:如果promise的状态不改变,then里的方法不会执行
解决:使用return可以将实例的状态改成fulfilled
1 2 3 4 5 6 7 8 9 10 11 12 const p = new Promise ((resolve,reject ) => { resolve (); }); const t = p.then (() => { console .log ('success' ); return 233 ; }); t.then ((value ) => { });
catch方法
1 2 3 4 5 6 7 const p = new Promise ((resolve,reject ) => { rejected (); }); const t = p.catch ((reason ) => { console .log ('reason:' +reason); });
当promise的状态改为rejected时,会执行
当promise的执行体中出现代码错误,会执行
数值扩展 Number.EPSILON是js表示的最小精度(其值为2.2204460……)【小数点后十六位】
在两个数比较大小时,如果这两个数的绝对值小于Number.EPSILON,则认为这两个数是相等的(有点像高数里的极限)
1 2 3 4 5 6 7 8 function equal (a, b ){ if (Math .abs (a-b) < Number .EPSILON ){ return true ; } else { return false ; } }
对象扩展
Object.assign(target, source)
两个对象target,source,source会覆盖target的对象属性
Object.setPrototypeOf()
设置对象的原型
模块化 模块化是指将一个大的文件拆分成许多小文件,然后将小文件结合起来
优点:
防止命名冲突
代码复用
高维护性
模块化语法 export:向外暴露接口
import:引入其他模块提供的功能
多种暴露的方式
分别暴露
1 2 export let param = 0 ;export function fun ( ){};
统一暴露
1 2 3 let param = 0 ; function fun ( ){}; export {param, fun}
默认暴露
多种导入方式
1 2 3 <script type="module" > import * as m1 from "js文件路径" ; </script>
解构赋值形式
1 2 3 4 5 6 7 import {想要从模块获取的变量或方法} from 'js路径' import {a} from 'js路径' import {a as bb} from 'js路径' import {default as m} from ‘./m.js ’
简便形式(只针对默认暴露)
1 import mm from ‘./mm.js ’
ES8 async && await
async函数
async函数的返回值为promise对象
promise对象的结果有async函数执行的返回值决定
await表达式
await必须写在async函数中
其右侧的表达式一般为promise对象
await返回的是promise成功的值
await的promise失败了,就会抛出异常,需要通过try-catch捕获处理
对象方法扩展
手撕Promise源码 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 function Promise (executor ){ this .PromiseState = 'pending' ; this .PromiseResult = null ; this .callback = {}; const _self = this ; function resolve (data ){ if (_self.PromiseState != 'pending' ) return ; _self.PromiseState = 'fulfilled' ; _self.PromiseResult = data; if (_self.callback .onResolved ){ _self.callback .onResolved (data); } } function reject (data ){ if (_self.PromiseState != 'pending' ) return ; _self.PromiseState = 'rejected' ; _self.PromiseResult = data; if (_self.callback .onRejected ){ _self.callback .onRejected (data); } } executor (resolve, reject); Promise .prototype .then = function (onResolved, onRejected ){ if (this .PromiseState === 'fulfilled' ){ onResolved (this .PromiseResult ); } if (this .PromiseState === 'rejected' ){ onRejected (this .PromiseResult ); } if (this .PromiseState === 'pending' ){ this .callback = { onRejected,onResolved } } } }
改进一 但是,我们发现使用上述代码时,如果调用多个then方法,会出现只执行最后一个then方法的结果,这是因为上述代码保存callback是以对象的形式保存的,每执行一次then,后一个then保存的callback会覆盖掉前一个保存的。因此代码可以进一步改进,就是将callback保存的形式改为数组,在调用时直接遍历,下面放上改进代码
1 2 3 4 5 6 7 8 9 10 11 12 this .callbacks = [];_self.callbacks .forEach ((item ) => {item.onResolved (data)}); _self.callbacks .forEach ((item ) => {item.onRejected (data)}); this .callbacks .push ({ onRejected,onResolved })
改进二 同步 修改状态then方法结果返回
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 Promise .prototype .then = function (onResolved, onRejected ){ return new Promise ((resolve, reject ) => { if (this .PromiseState === 'fulfilled' ){ let result = onResolved (this .PromiseResult ); if (result instanceof Promise ){ result.then (v => { resolve (v); },r => { reject (r); }); } else { resolve (result); } } if (this .PromiseState === 'rejected' ){ onRejected (this .PromiseResult ); } if (this .PromiseState === 'pending' ){ this .callbacks .push ({ onRejected,onResolved }) } }) }
then方法返回的结果是一个promise对象,而这个promise对象的结果是由其指定的回调函数决定的
如果抛出异常,返回的promise变为rejected,reason为抛出的异常
如果返回的是非promise的任意值,返回的promise的值变为resolved,value为返回的值
如果返回的是另一个新的promise,则这个的promise的结果会变成返回的promise的结果
异步修改状态then方法结果返回
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 Promise .prototype .then = function (onResolved, onRejected ){ const self = this ; return new Promise ((resolve, reject ) => { if (this .PromiseState === 'fulfilled' ){ let result = onResolved (this .PromiseResult ); if (result instanceof Promise ){ result.then (v => { resolve (v); },r => { reject (r); }); } else { resolve (result); } } if (this .PromiseState === 'rejected' ){ let result = onRejected (this .PromiseResult ); if (result instanceof Promise ){ result.then (v => { resolve (v); },r => { reject (r); }); } else { reject (result); } } if (this .PromiseState === 'pending' ){ this .callbacks .push ({ onResolved : function ( ){ let result = onResolved (self.PromiseResult ); if (result instanceof Promise ){ result.then (v => { resolve (v); },r => { reject (r); }); } else { resolve (result); } }, onRejected : function ( ){ let result = onRejected (self.PromiseResult ); if (result instanceof Promise ){ result.then (v => { resolve (v); },r => { reject (r); }); } else { resolve (result); } } }) } }) }
注:未完待续,回头再研究其他方法的源码 axios 定义:Promise based HTTP client for the browser and node.js
用于发送ajax请求(browser)
用于发送http请求(node.js)
可以使用 函数axios()发送ajax请求 1 2 3 4 5 6 7 8 9 btns[0 ].onclick = function ( ){ axios ({ method : 'GET' , url : 'http://localhost:3000/comments/1' }).then (repsonse => { console .log (repsonse); }) };
除了发送get请求,还可发送post,put,delete请求
另外,除了使用上述的方法发送请求,还可以使用axios对象发送请求
axios.request(config)
axios.post(url, data, config)
…
需要注意的是config指的是一个配置对象,我们可以设置一些属性,比如:请求方式,baseURL,设置超时等
创建实例对象发送请求 我们可以使用axios的create()方法,该方法接收一个配置对象config,并返回一个与axios相近的对象
Q:为什么要创建实例对象发送请求呢?
A:在实际项目中,页面一般会从不同的服务器发送请求,axios的配置对象就会随着服务器的不同而变化。因此创建不同的实例对象,每个实例对象负责一个服务器,各司其职
axios拦截器 axios取消请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let cancel = null ;btns[4 ].onclick = function ( ){ if (cancel != null ){ cancel (); }; axios ({ method : 'GET' , url : 'http://localhost:3000/comments/1' , cancelToken : new axios.CancelToken (function (c ){ cancel = c; }) }).then (repsonse => { console .log (repsonse); console .log (cancel); cancel = null ; }); };