在display:flex基础上使用flex-direction,可用于多子模块布局
flex-direction可以为四个值
row:水平并行排列在左侧
row-reverse:水平并行排列在右侧,顺序与书写顺序相反,最前的在最右边
column:垂直排列在上方,相当于display:block状态下模块默认布局
column-reverse:垂直排列在下方,顺序与书写顺序相反,最前的在最下边
1 | <!doctype html> |
learn and share
在display:flex基础上使用flex-direction,可用于多子模块布局
flex-direction可以为四个值
row:水平并行排列在左侧
row-reverse:水平并行排列在右侧,顺序与书写顺序相反,最前的在最右边
column:垂直排列在上方,相当于display:block状态下模块默认布局
column-reverse:垂直排列在下方,顺序与书写顺序相反,最前的在最下边
1 | <!doctype html> |
1 | let arr = [1,2,3,4,5]; |
在map中可以传递一个函数,数组的每一项作为参数调用这个函数,然后返回根据该数组项得到的结果,每一个数组项返会一个结果,从而组成新的数组
函数的参数有三个,第一项为数组按序传进的一个值,该值的index和数组本身
整个结果产生新数组,原来数组不变
1 | 例1. |
reduce 用于对数组每一项进行累计计算,每一项计算结果作为参数参加数组下一项的计算,最终返回计算结果
例1中单纯对数组每一项进行叠加处理,返回叠加结果
例2同样进行叠加只是叠加结果放在对象中返回
reduce(callback(),initval)
需要两个参数,一个是用于累计计算的callback函数,一个是初始值,见上两例
callback()函数有四个参数依次为:
preVal (上一次调用回调返回的值,或者是提供的初始值(initialValue))
currentValue (数组中当前被处理的元素)
index (当前元素在数组中的索引)
array (调用 reduce 的数组)
初始值会在第一次调用时当作回调函数callback的第一个参数使用
希望返回值是什么类型的数据,初始值就要设置为什么样的类型,callback返回值也是该类型;或者说是在这里设置返回值的类型
整个处理过程就是拿初始值initval和数组第一个值在callback里面进行处理,返回结果result,result当作callback的preval参数和数组第二个元素传入callback在处理,直到数组全部元素被处理完,返回一个最终处理结果
高级应用
对对象数组元素进行累计计算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
27var reducers = {
totalInEuros : function(state, item) {
return state.euros += item.price * 0.897424392;
},
totalInYen : function(state, item) {
return state.yens += item.price * 113.852;
}
};
var manageReducers = function(reducers) {
return function(state, item) {
return Object.keys(reducers).reduce(
function(nextState, key) {
reducers[key](state, item);
return state;
},
{}
);
}
};
var bigTotalPriceReducer = manageReducers(reducers);
var initialState = {euros:0, yens: 0};
var items = [{price: 10}, {price: 120}, {price: 1000}];
var totals = items.reduce(bigTotalPriceReducer, initialState);
console.log(totals);
//{euros: 1014.08956296, yens: 128652.76}
该例的目的是将对象数组items的元素属性price进行不同处理操作然后将结果分别保存到结果对象的属性中
处理的关键是manageReducers返回的作为items callback的函数bigTotalPriceReducer,
处理思路是items每用一个元素调用callback时,callback的处理过程是将这个元素作为参数调用reducers 对象里面设置的每一个处理函数,
调用reducers处理函数的过程同样使用一个reduce方法进行处理
Object.keys(obj)方法返回对象键值组成的数组
当items的元素参加完reducers所有处理函数后,返回的结果参加下一个元素的计算最终返回结果
扩展:
1.在reduces中设置对items不同属性的计算,从而得到不同属性的结算结果
1 | var reducers = { |
2.计算对象数组属性值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var manageReducers = function(state, item) {
return Object.keys(item).reduce(
function(nextState, key) {
state[key] +=item[key];
return state;
},
{}
);
};
var initialState = {price:0, p1: 0};
var items = [{price: 10,p1:1}, {price: 120,p1:1}, {price: 1000,p1:1}];
var totals = items.reduce(manageReducers, initialState);
console.log(totals);
//{price: 1130, p1: 3}
async :立即异步下载,因此不保证能够按指定的先后顺序执行,但会在load事件前,DOMContentLoad前后执行
defer :延迟执行,也不保证按顺序执行,在DOMContentLoad前后执行
src :外部文件链接
charset :src指定代码的字符集,防止出现乱码
language :已废弃,表示脚本语言,
type :值为脚本语言内容类型(MIME),language替代属性,服务器传递过来类型为application/x-javascript需要注意,一个是可能导致脚本被忽略,另一个是可能触发浏览器XHTML标准模式;另外外链文件不是js文件时,一定要设置该值
包括两个部分一个是编译,或者叫预处理过程,一个是执行
编译过程 首先会创建一个全局作用域,然后进行词法分析,
词法分析过程会涉及到变量提升和函数声明提前处理,
即词法分析到变量声明会先判断当前域到全局域有没有该变量声明,如果有,就会将该变量挂到该作用域的对象上,
如果到全局域都没有则以全局为背景,新建该变量,并为其分配内存,
对于基本类型,变量中会保存它的值,对于对象,变量中会保存值所在内存的内存地址,
在一定场景下,这里就相当于发生了变量提升,把变量从局部提升到了全局,或者将声明提前到了使用之前,
避免变量提升可以使用es6的let 关键字或者使用立即执行实现变量声明使用,但要注意使用let重复声明会报错,var 会同名覆盖;
当遇到函数声明时,会直接按声明顺序,将声明提前到代码顶端,
重复声明 的函数会被后者覆盖,
如果变量名与函数名相同,则无论原来词法位置如何,先将函数声明提前到顶端,不再进行变量声明,变量名在哪赋值,则原函数在哪被覆盖,
覆盖之前调用使用原函数
提前之后会为函数创建作用域,
这时创建的作用域就是函数的 【【scope】】属性,保存着指向父域到全局域的指针
(指针就是指向活动对象的一个地址,全局域浏览器环境就是windows,nodejs环境就是global)
将来执行时会根据此指针建立作用域链,作用域链就是指针列表,指向不同层级作用域的内存,在里面取值
以上就基本上是编译过程,除了一些特定的编译规则,编译过程还会会使用大量技巧进行优化提升编译速度
但在使用js时,需要注意try-catch,with和eval三个语法关键字的使用,因为在使用它们时,会创建块级作用域,
破坏掉编译器原有的创建管理作用域的规则,在编译时,编译器也就不会对其中的代码进行优化,最终导致性能变差
运行过程,基本就是在运行函数,函数在运行时会首先根据【【scope】】属性拿到父域到全局的指针,创建指针列表即作用域链,
然后创建函数执行的局部作用域,将函数中声明的变量挂到局部作用域活动对象上,然后将局部作用域指针推向作用域链前端,
在用到变量值时,会根据作用域链依次查找取值
函数执行结束后,局部活动对象被销毁,作用域链被销毁,进行垃圾回收
但是对于声明在一个函数内部的函数来说,这个声明的函数在声明时,就会包含其父域的活动对象,
因此在外部使用该函数时,可以访问到其父域的变量,实现了父域访问子域变量,延长了变量的使用范围,也就是常见的闭包现象,
缺点就是闭包的函数执行完毕后,因为本身【【scope】】会指向父域的活动对象,所以父域的活动对象始终不会被回收,除非闭包函数被销毁
从而有可能造成内存泄露,为什么说有可能,因为父活动对象占的内存其实是比较小的,造成内存泄露的真正原因是使用闭包易造成循环引用,
尤其是闭包作用域中保存dom结点时,如果是在IE中,dom和bom都是以C++的COM对象保存的,垃圾回收机制是技术策略,
因此循环引用的话,永远都不会被回收
补充,对于未以分号结尾的语句,会采用能合就合的原则,即,会尝试和下一行语句合并执行,如果能合并执行就合并执行,
不能合并执行就在中间加分号再执行,如果再执行不了就会报错
好处就是对于条件判断语句可进行一行一行判断
坏处就是如果下一行以大括号开头,上一行语句就会变成函数,导致出现意想不到的结果
两个例外,
对于return,break,continue语句不会做尝试合并;
对于++,–自增自减符后没有分号,上一行也没分号情况,自增自减会和下一行合并,上一行自行添加分号
以上即是我所了解的js运行机制
3.0 new 操作符调用构造函数 执行过程
1.创建或者构造一个全新的对象
2.将构造函数的作用域赋给这个新的对象,
3.执行构造函数 (为这个新对象添加属性) ,第二,三步相当于func.call(newobj)
4.如果构造函数没有返回值或返回值非对象,则返回新对象,否则返回构造函数return的对象
3.1 继承的6种方式及优缺点
第一种原型链方式,子类的原型对象指向父类的实例
缺点
给原型添加方法必须要在替换原型的语句之后;
不能实现多继承(原型指向多个父对象);
所有属性共享
无法传递参数到父类进行初始化
第二种借助构造函数,在子类构造函数中使用call或者apply执行父类构造函数
优点
可以实现多继承
可以传递参数
方法属性不共享
缺点
只能继承构造函数中属性,不能继承原型上方法,不能复用方法
实例仅为子类实例,不是父类实例
第三种组合继承,以上两组结合在一起,两种方式都执行
缺点
要调用两次父类构造函数,影响性能
实例属性和原型属性各占一份,同名覆盖机制,重复占内存,没必要
第四种原型式继承,借助object()或者object.create()函数,缺点同原型链方式
1 | function object(o){ |
第五种寄生式继承,利用原型式继承和增强对象过程的封装函数创建子类,缺点同原型链方式
第六种寄生组合式继承,保留借助构造函数部分,原型部分功能借助中间函数处理,
中间函数借助object拿到父类prototype,给到子类的prototype,从而完美解决组合继承的缺点
1 | function inheritPrototype(subType,superType){ |
3.2 es6 使用class extend实现继承
使用class声明构造函数时,本来挂在this上的属性,放在constructor()函数里面执行初始化,
挂在prototype上的方法直接放在class里面声明
使用extend继承时,需要在constructor里面先执行super函数才能使用this关键字,
增强方法同样直接在class中和constructor并列声明,相当于挂在prototype上
注意super函数只能在consructor中运行;super用作对象在constructor中使用时,相当于this,
添加属性会添加在当前对象上,运行函数时会调用继承的父类的方法
还有就是class extend继承null和 什么也不继承是,相当于继承Function.prototype
继承Object时,子类就相当于object 的复制,子类实例就是object实例
普通函数执行,指向全局作用域
对象属性方法调用,指向调用对象
call,apply中强制绑定对象,this执行绑定的对象
构造函数中this指向构造的新对象
(箭头函数中this,指向词法作用域的父域)
放html底部原因:
下载解析执行过程是阻断式的,会停止页面渲染,造成白屏,影响用户体验;
另外JS中如果有对dom的操作,页面中还没有dom,获取不到,是不符合逻辑的,会报错
哪些js功能文件可以放顶部:
与css相关的js,比如rem单位的换算,需要根据根结点进行设置
使用的框架需要在根结点将浏览器支持的样式罗列出来
内部文件相比外部文件最大好处就是性能提升,因为访问外部文件时,不管文件大小都会造成一次网络请求链接,请求服务器,下载文件,
解析文件,除了文件本身代码外,还要处理文件头文件尾的请求,增加了链接数,从而造成性能影响
对于功能短小精悍,不会被到处复用的js代码不适合采用外部文件,应该采用内部文件写法,尤其对手机端页面有性能提升
在浏览器开启禁用缓存的模式
手动清缓存 ctrl+shfit+del
引用的文件名添加随机数,浏览器根据文件名不同,就会重新获取资源更新缓存
在代码压缩时,’use strict’ 这一行可能不在位于第一行,
或者后续代码不需要在严格模式下执行,被压缩在了严格模式范围内,导致了全部以严格模式执行
解决办法就是,将需要严格模式执行的代码放在匿名函数中,形成代码块,在函数中使用严格模式
避免变量污染:有些变量仅在小功能内使用,将小功能封装起来,就可以避免这些变量暴露到全局,
另外函数本身没有函数名,不会增加全局变量
提升性能:功能所需变量全都在函数内时,查找变量快
有利于压缩:一部分代码执行需要用到的变量变量名太长,将这段代码封装成立即执行函数,将长变量名以参数形式传进去
避免全局命名冲突:一段代码需要用到两个代表不同功能但名称相同的变量,可以将该段代码封装成立即执行函数,
将其中一个变量以参数形式传递进来,达到换名的目的
保存闭包状态:循环执行异步代码时,将异步代码用立即执行函数包裹,函数内可保存本次循环的状态
改变代码运行顺序:umd,通用模块规范,function(fn){fn()}()
根据当前变量的值的类型确定
如果想要进行标记,方法有三种
初始化时指定相应类型值
使用匈牙利标记法,用单字母表示类型,添加到变量命中,例sName 代表string类型
使用注释,变量声明时,在旁边添加注释
转数字时,null –>0;undefined–>NaN
undefined可以当做windows对象上的一个属性,null不行
基本类型包括,undefined,null,boolean,string,number
基本类型 —> boolean Boolean(someval) 相当于 !!someval
undefined,null,NaN,’’,+0,-0 —> false
其他 —> true
基本类型 —> string String(someval)相当于 someval+’’
一般情况直接加引号
[] —> ‘’
{} —> ‘[object object]’
基本类型—>number + someval 相当于 Number(someval)
undefined,{} —> NaN
false, null,’’,[],—> 0
true —> 1
‘123’ —> 123
‘abc’ —> NaN
[]—> toString —> ‘’ —> 0
{} —> valueOf —> {} —> toString —> ‘[object object]’ —> NaN
基本类型 —>对象
undefined,null —> {}
true/123 —> {initval:true/123}
‘abc’ —> {initval:’abc’,length:3,0:’a’,1:’b’,2:’c’}
对象 —>基本类型
关于–对象分类:
内部对象(错误对象;常用对象:Boolean,Number,String,Object,Array,Function,Date,Exec;内置对象:Math,global,Json)
宿主对象:windows,document
自定义对象
关于–属性访问
obj.someprop—>检查obj是不是null/undefined—>是|不是—>报错|是不是Object,不是的话转object—>取值
obj[someval]—>检查obj是不是null/undefined—>是|不是—>报错|是不是Object,不是的话转object—>计算someval拿到string—>取值
Boolean —>true
Number —>基本类型转的|{}/{a:1}|Date—>相应值|NaN|时间戳
Array.toString:数组每一项转字符串
Function.toString:原代码字符串形式
Date.toString:日期和时间组合形式
Exec.toString:正则字符串形式
运算转换
Number()参数不能有非数字
parseInt()参数只能为字符串,非字符串会进行强制转化字符串,可含非数字,但不能以非数字开头
一元操作符对任何数据类型有效,得到数字
+二元运算侧重转字符串,然后进行字符串拼接
其中一个为字符串或者两个都是对象时,转字符串拼接
undefined,null,Boolean,数字混合运算+时,会被转数字再计算
{}为第一个运算值时,会被当做函数结束,不参与计算1
2
3
4
5
6
7
8
9
10
11
12null + null --> 0
undefined + null --> NaN
[] + [] --> ''
1 + [1,2] --> '11,2'
1 + {a:1} --> '1[object object]'
{a:1} + 1 --> 1
{} + {} --> NaN
({}) + {} --> '[object object][object object]'
[] + {} --> '[object object]'
{} + [] --> 0
var a = {}
a + a -->'[object object][object object]'
*/- 转数字,进行相减,如果不能转数字,返回NaN
>/< 侧重转数字
string < string 字典比较
非字符串<非字符串/字符串 都转数字然后比较
string == number 字符串转数字
boolean == 其他类型 二者转数字然后比较
null == undefined —>true undefined和null只与对方和自己==比较时为true,其他均为false
null === undefined —>false
非对象 == 对象 与数字比较object转数字,与字符串比较二者转数字再比较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'0' == null //false
'0' == undefined //false
'0' == false //true
'0' == NaN //false
'0' == 0 //true
'0' == '' //true
false == null //false
false == undefined //false
false == NaN //false
false == 0 //true
false == '' //true
false == [] //true
false == {} //false
'' == null //false
'' == undefined //false
'' == NaN //false
'' == 0 //true
'' == [] //true
'' == {} //false
0 == null //false
0 == undefined //false
0 == NaN //false
0 == [] //true
0 == {} //false
[] == ![]
&&和|| 计算结果根据短路原则判断到了哪里,返回最后一个进行判断的值
运算符优先级
属性访问.
一元操作符(+,-,++,–)
二元操作符(+-*/)
比较(>,<)
相等(==,===,!=)
与&&,或||
三目运算符
赋值
运算符结合性
除了一元操作符,三目运算符和赋值是右结合,其他都是左结合
减少小数计算误差
尽量不使用小数进行比较或运算
转整数计算,再转回相应位数小数
使用toFixed()四舍五入
两个情境需要重复计算时,保证前后书写顺序


一共 33个
set/get(UTC)FullYear,Month,Date,Hours,Minutes,Seconds,Millseconds
set/get Time
get(UTC) Day
getTimezoneOffset
Math.ceil:比值大的最小整数
Math.round:四舍五入
Math.floor:比值小的最大整数
累积效应
代码执行时间大于间隔时间,后续调用会进行累积,累积会在短时间内连续触发
当用于动画时,因与显示器刷新频率不统一会造成视觉卡顿,解决办法如下:
一种是使用CSS3创建动画,根据显示器刷新执行动画
另一种是使用 requestAnimationframe(function(){})函数,也是在显示器刷新时执行
可以解决CSS3无法实现的,例如滚动控制等效果
document 三个属性
url:地址栏中URL
domain:域名,跨域域名 X;父域名到子域名 X;
referer:上一页URL,可在跳转时,判断与当前页是否在同一域,是的话,就可以back,否则 location.href其他页面
dom.getAttribute(‘style’),得到样式字符串
dom.style 得到到样式对象
dom.getAttribute(‘onclick’) ,得到代码字符串
dom.onclick 得到函数对象
动态合集
使用时可能会造成死循环
使用框架如Jquery获取的合集不具有动态性
使用 queryselect(all)()获取到的也是静态合集
动态合集生成原理:
浏览器通过DOM树预先缓存起来,获取时,浏览器会通过缓存直接注册,创建一个变量返回
静态合集生成原理:
获取标识是一个CSS选择器,浏览器会先去解析字符串,判断是一个CSS选择器,
分析选择器并创建一个选择器结构,浏览器根据DOM树创建一个静态文件,即DOM树的一个快照
然后拿选择器和快照进行对比,符合选择器就放到合集中,知道整个快照对比完,再把合集的快照返回
绑定方式
直接通过HTML属性绑定
缺点
有可能响应函数还未被解析就被触发
事件名和回调1对1,修改繁琐
响应执行时,作用域为全局,不同浏览器解析规则不同,造成某些对象无法访问
使用dom属性绑定
冒泡阶段执行
缺点
只能绑定一个响应,后续赋值会被覆盖
IE attachEvent/detachEvent绑定/解绑
冒泡阶段执行
绑定多个时按按绑定顺序的逆序执行
addEventListener()/removeEventListener()绑定/解绑
第三个参数为对象时,passive属性为true时,会针对touchstart,touchend等特定事件通过开启两个线程,
一个执行浏览器默认行为,一个执行JS进行优化
事件优先级
浏览器在绑定事件而不是JS在绑定事件
1.html标签绑定事件会被dom属性事件覆盖
2.html标签优先执行,即使被属性事件覆盖,则执行被覆盖的属性事件
3.仅有监听事件和属性事件时,按绑定顺序执行,即事件对象的冒泡事件可能会在捕获事件之前执行
event对象属性
curentTarget:响应函数绑定的对象
target:发生事件的对象
1.jquery对象与Element对象转换1
2
3
4
5
6
7//Element对象转jquery对象
var domObj = document.getElementById('id')
var $obj = $(domObj)//jQuery对象
//jquery对象转Element对象
var $box = $('.box')
var box = $box[0]
2.常用jQuery选择器接口
//传入对象 , 例$(document),把传入的对象包装成jQuery对象
$(this}
//传入函数 , 这个是在页面DOM文档加载完成后加载执行的,等效于在DOM加载完毕后执行了${document).ready()方 法。
$ (function (){})
//传入字符串 ,査询D0M节点包装成jQuery对象
$(.box)
//传入HTML字符串 创建DOM节点包装成jQuery对象
$(<div>)
//空
$()创建jQuery对象
3.如何把创建的Dom结点包装成jQuery对象
contetx.createElement创建DOM节点存储在数组中,调用merge方法把数组中存储的DOM节点的成员添加到jQuery实例对象上
4.jQuery实例对象length属性作用
存储DOM节点的数组对象平滑地添加到jQuery实例对象上
5.merge方法应用场景有哪些
合并数组
把数组成员合并在有length属性的对象上
6.$(document).ready()与$(function(){})的关系
$(document).ready()是对document.DOMContentLoaded事件的封装
$(function(){})每次调用$()传入的参数会收集在readyList数组中,
当document.DOMContentLoaded事件触发时依次执行readyList中收集的处理函数
async :立即异步下载,因此不保证能够按指定的先后顺序执行,但会在load事件前,DOMContentLoad前后执行
defer :延迟执行,也不保证按顺序执行,在DOMContentLoad前后执行
src :外部文件链接
charset :src指定代码的字符集,防止出现乱码
language :已废弃,表示脚本语言,
type :值为脚本语言内容类型(MIME),language替代属性,服务器传递过来类型为application/x-javascript需要注意,一个是可能导致脚本被忽略,另一个是可能触发浏览器XHTML标准模式;另外外链文件不是js文件时,一定要设置该值
包括两个部分一个是编译,或者叫预处理过程,一个是执行
编译过程 首先会创建一个全局作用域,然后进行词法分析,
词法分析过程会涉及到变量提升和函数声明提前处理,
即词法分析到变量时会先判断当前域到全局域有没有该变量声明,如果有,就会将该声明的变量挂到该作用域的对象上,
如果到全局域都没有则以全局为背景,新建该变量,并为其分配内存,
对于基本类型,变量中会保存它的值,对于对象,变量中会保存值所在内存的内存地址,
在一定场景下,这里就相当于发生了变量提升,把变量从局部提升到了全局,或者将声明提前到了使用之前,
避免变量提升可以使用es6的let 关键字或者使用立即执行实现变量声明使用,但要注意使用let重复声明会报错,var 会同名覆盖;
当遇到函数声明时,会直接按声明顺序,将声明提前到代码顶端,
重复声明 的函数会被后者覆盖,
如果变量名与函数名相同,则无论原来词法位置如何,先将函数声明提前到顶端,不再进行变量声明,变量名在哪赋值,则原函数在哪被覆盖,
覆盖之前调用使用原函数
提前之后会为函数创建作用域,
这时创建的作用域就是函数的 【【scope】】属性,保存着指向父域到全局域的指针
(指针就是指向活动对象的一个地址,全局域浏览器环境就是windows,nodejs环境就是global)
将来执行时会根据此指针建立作用域链,作用域链就是指针列表,指向不同层级作用域的内存,在里面取值
以上就基本上是编译过程,除了一些特定的编译规则,编译过程还会会使用大量技巧进行优化提升编译速度
但在使用js时,需要注意try-catch,with和eval三个语法关键字的使用,因为在使用它们时,会创建块级作用域,
破坏掉编译器原有的创建管理作用域的规则,在编译时,编译器也就不会对其中的代码进行优化,最终导致性能变差
运行过程,基本就是在运行函数,函数在运行时会首先根据【【scope】】属性拿到父域到全局的指针,创建指针列表即作用域链,
然后创建函数执行的局部作用域,将函数中声明的变量挂到局部作用域活动对象上,然后将局部作用域指针推向作用域链前端,
在用到变量值时,会根据作用域链依次查找取值
函数执行结束后,局部活动对象被销毁,作用域链被销毁,进行垃圾回收
但是对于声明在一个函数内部的函数来说,这个声明的函数在声明时,就会包含其父域的活动对象,
因此在外部使用该函数时,可以访问到其父域的变量,实现了父域访问子域变量,延长了变量的使用范围,也就是常见的闭包现象,
缺点就是闭包的函数执行完毕后,因为本身【【scope】】会指向父域的活动对象,所以父域的活动对象始终不会被回收,除非闭包函数被销毁
从而有可能造成内存泄露,为什么说有可能,因为父活动对象占的内存其实是比较小的,造成内存泄露的真正原因是使用闭包易造成循环引用,
尤其是闭包作用域中保存dom结点时,如果是在IE中,dom和bom都是以C++的COM对象保存的,垃圾回收机制是计数策略,
因此循环引用的话,永远都不会被回收
补充,对于未以分号结尾的语句,会采用能合就合的原则,即,会尝试和下一行语句合并执行,如果能合并执行就合并执行,
不能合并执行就在中间加分号再执行,如果再执行不了就会报错
好处就是对于条件判断语句可进行一行一行判断
坏处就是如果下一行以大括号开头,上一行语句就会变成函数,导致出现意想不到的结果
两个例外,
对于return,break,continue语句不会做尝试合并;
对于++,–自增自减符后没有分号,上一行也没分号情况,自增自减会和下一行合并,上一行自行添加分号
以上即是我所了解的js运行机制
1 | //补充1 |
3.0 new 操作符调用构造函数 执行过程
1.创建或者构造一个全新的对象
2.将构造函数的作用域赋给这个新的对象,
3.执行构造函数 (为这个新对象添加属性) ,第二,三步相当于func.call(newobj)
4.如果构造函数没有返回值或返回值非对象,则返回新对象,否则返回构造函数return的对象
3.1 继承的6种方式及优缺点
第一种原型链方式,子类的原型对象指向父类的实例
缺点
给原型添加方法必须要在替换原型的语句之后;
不能实现多继承(原型指向多个父对象);
所有属性共享
无法传递参数到父类进行初始化
第二种借助构造函数,在子类构造函数中使用call或者apply执行父类构造函数
优点
可以实现多继承
可以传递参数
属性不共享
缺点
只能继承构造函数中属性,不能继承原型上方法,不能复用方法
实例仅为子类实例,不是父类实例
第三种组合继承,以上两组结合在一起,两种方式都执行
缺点
要调用两次父类构造函数,影响性能
实例属性和原型属性各占一份,同名覆盖机制,重复占内存,没必要
第四种原型式继承,借助object()或者object.create()函数,缺点同原型链方式
1 | function object(o){ |
第五种寄生式继承,利用原型式继承和增强对象过程的封装函数创建子类,缺点同原型链方式
第六种寄生组合式继承,保留借助构造函数部分,原型部分功能借助中间函数处理,
中间函数借助object拿到父类prototype,给到子类的prototype,从而完美解决组合继承的缺点
1 | function inheritPrototype(subType,superType){ |
3.2 es6 使用class extend实现继承
使用class声明构造函数时,本来挂在this上的属性,放在constructor()函数里面执行初始化,
挂在prototype上的方法直接放在class里面声明
使用extend继承时,需要在constructor里面先执行super函数才能使用this关键字,
增强方法同样直接在class中和constructor并列声明,相当于挂在prototype上
注意super函数只能在consructor中运行;super用作对象在constructor中使用时,相当于this,
添加属性会添加在当前对象上,运行函数时会调用继承的父类的方法
还有就是class extend继承null和 什么也不继承是,相当于继承Function.prototype
继承Object时,子类就相当于object 的复制,子类实例就是object实例
普通函数执行,指向全局作用域
对象属性方法调用,指向调用对象
call,apply中强制绑定对象,this执行绑定的对象
构造函数中this指向构造的新对象
(箭头函数中this,指向词法作用域的父域)
放html底部原因:
下载解析执行过程是阻断式的,会停止页面渲染,造成白屏,影响用户体验;
另外JS中如果有对dom的操作,页面中还没有dom,获取不到,是不符合逻辑的,会报错
哪些js功能文件可以放顶部:
与css相关的js,比如rem单位的换算,需要根据根结点进行设置
使用的框架需要在根结点将浏览器支持的样式罗列出来
内部文件相比外部文件最大好处就是性能提升,因为访问外部文件时,不管文件大小都会造成一次网络请求链接,请求服务器,下载文件,
解析文件,除了文件本身代码外,还要处理文件头文件尾的请求,增加了链接数,从而造成性能影响
对于功能短小精悍,不会被到处复用的js代码不适合采用外部文件,应该采用内部文件写法,尤其对手机端页面有性能提升
在浏览器开启禁用缓存的模式
手动清缓存 ctrl+shfit+del
引用的文件名添加随机数,浏览器根据文件名不同,就会重新获取资源更新缓存
在代码压缩时,’use strict’ 这一行可能不在位于第一行,
或者后续代码不需要在严格模式下执行,被压缩在了严格模式范围内,导致了全部以严格模式执行
解决办法就是,将需要严格模式执行的代码放在匿名函数中,形成代码块,在函数中使用严格模式
避免变量污染:有些变量仅在小功能内使用,将小功能封装起来,就可以避免这些变量暴露到全局,
另外函数本身没有函数名,不会增加全局变量
提升性能:功能所需变量全都在函数内时,查找变量快
有利于压缩:一部分代码执行需要用到的变量变量名太长,将这段代码封装成立即执行函数,将长变量名以参数形式传进去
避免全局命名冲突:一段代码需要用到两个代表不同功能但名称相同的变量,可以将该段代码封装成立即执行函数,
将其中一个变量以参数形式传递进来,达到换名的目的
保存闭包状态:循环执行异步代码时,将异步代码用立即执行函数包裹,函数内可保存本次循环的状态
改变代码运行顺序:umd,通用模块规范,function(fn){fn()}()
根据当前变量的值的类型确定
如果想要进行标记,方法有三种
初始化时指定相应类型值
使用匈牙利标记法,用单字母表示类型,添加到变量命中,例sName 代表string类型
使用注释,变量声明时,在旁边添加注释
转数字时,null –>0;undefined–>NaN
undefined可以当做windows对象上的一个属性,null不行
基本类型包括,undefined,null,boolean,string,number
基本类型 —> boolean Boolean(someval) 相当于 !!someval
undefined,null,NaN,’’,+0,-0 —> false
其他 —> true
基本类型 —> string String(someval)相当于 someval+’’
一般情况直接加引号
[] —> ‘’
{} —> ‘[object object]’
基本类型—>number + someval 相当于 Number(someval)
undefined,{} —> NaN
false, null,’’,[],—> 0
true —> 1
‘123’ —> 123
‘abc’ —> NaN
[]—> toString —> ‘’ —> 0
{} —> valueOf —> {} —> toString —> ‘[object object]’ —> NaN
基本类型 —>对象
undefined,null —> {}
true/123 —> {initval:true/123}
‘abc’ —> {initval:’abc’,length:3,0:’a’,1:’b’,2:’c’}
对象 —>基本类型
关于–对象分类:
内部对象(错误对象;常用对象:Boolean,Number,String,Object,Array,Function,Date,Exec;内置对象:Math,global,Json)
宿主对象:windows,document
自定义对象
关于–属性访问
obj.someprop—>检查obj是不是null/undefined—>是|不是—>报错|是不是Object,不是的话转object—>取值
obj[someval]—>检查obj是不是null/undefined—>是|不是—>报错|是不是Object,不是的话转object—>计算someval拿到string—>取值
Boolean —>true
Number —>基本类型转的|{}/{a:1}|Date—>相应值|NaN|时间戳
Array.toString:数组每一项转字符串
Function.toString:原代码字符串形式
Date.toString:日期和时间组合形式
Exec.toString:正则字符串形式
运算转换
Number()参数不能有非数字
parseInt()参数只能为字符串,非字符串会进行强制转化字符串,可含非数字,但不能以非数字开头
一元操作符对任何数据类型有效,得到数字
+二元运算侧重转字符串,然后进行字符串拼接
其中一个为字符串或者两个都是对象时,转字符串拼接
undefined,null,Boolean,数字混合运算+时,会被转数字再计算
{}为第一个运算值时,会被当做函数结束,不参与计算1
2
3
4
5
6
7
8
9
10
11
12null + null --> 0
undefined + null --> NaN
[] + [] --> ''
1 + [1,2] --> '11,2'
1 + {a:1} --> '1[object object]'
{a:1} + 1 --> 1
{} + {} --> NaN
({}) + {} --> '[object object][object object]'
[] + {} --> '[object object]'
{} + [] --> 0
var a = {}
a + a -->'[object object][object object]'
*/- 转数字,进行相减,如果不能转数字,返回NaN
>/< 侧重转数字
string < string 字典比较
非字符串<非字符串/字符串 都转数字然后比较
string == number 字符串转数字
boolean == 其他类型 二者转数字然后比较
null == undefined —>true undefined和null只与对方和自己==比较时为true,其他均为false
null === undefined —>false
非对象 == 对象 与数字比较object转数字,与字符串比较二者转数字再比较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'0' == null //false
'0' == undefined //false
'0' == false //true
'0' == NaN //false
'0' == 0 //true
'0' == '' //false
false == null //false
false == undefined //false
false == NaN //false
false == 0 //true
false == '' //true
false == [] //true
false == {} //false
'' == null //false
'' == undefined //false
'' == NaN //false
'' == 0 //true
'' == [] //true
'' == {} //false
0 == null //false
0 == undefined //false
0 == NaN //false
0 == [] //true
0 == {} //false
[] == ![] //true
&&和|| 计算结果根据短路原则判断到了哪里,返回最后一个进行判断的值
运算符优先级
属性访问.
一元操作符(+,-,++,–)
二元操作符(+-*/)
比较(>,<)
相等(==,===,!=)
与&&,或||
三目运算符
赋值
运算符结合性
除了一元操作符,三目运算符和赋值是右结合,其他都是左结合
减少小数计算误差
尽量不使用小数进行比较或运算
转整数计算,再转回相应位数小数
使用toFixed()四舍五入
两个情境需要重复计算时,保证前后书写顺序

1
2let arr = []
let iterator = arr[Symbol.iterator]() //可以获取数组迭代器
遍历对象/数组的方法
for
while(do…while)
forEach
map
reduce
for…of
for…in
iterator
generator

一共 33个
set/get(UTC)FullYear,Month,Date,Hours,Minutes,Seconds,Millseconds
set/get Time
get(UTC) Day
getTimezoneOffset
Math.ceil:比值大的最小整数
Math.round:四舍五入
Math.floor:比值小的最大整数
累积效应
代码执行时间大于间隔时间,后续调用会进行累积,累积会在短时间内连续触发
当用于动画时,因与显示器刷新频率不统一会造成视觉卡顿,解决办法如下:
一种是使用CSS3创建动画,根据显示器刷新执行动画
另一种是使用 requestAnimationframe(function(){})函数,也是在显示器刷新时执行
可以解决CSS3无法实现的,例如滚动控制等效果
document 三个属性
url:地址栏中URL
domain:域名,跨域域名 X;父域名到子域名 X;
referer:上一页URL,可在跳转时,判断与当前页是否在同一域,是的话,就可以back,否则 location.href其他页面
dom.getAttribute(‘style’),得到样式字符串
dom.style 得到到样式对象
dom.getAttribute(‘onclick’) ,得到代码字符串
dom.onclick 得到函数对象
动态合集
使用时可能会造成死循环
使用框架如Jquery获取的合集不具有动态性
使用 queryselect(all)()获取到的也是静态合集
动态合集生成原理:
浏览器通过DOM树预先缓存起来,获取时,浏览器会通过缓存直接注册,创建一个变量返回
静态合集生成原理:
获取标识是一个CSS选择器,浏览器会先去解析字符串,判断是一个CSS选择器,
分析选择器并创建一个选择器结构,浏览器根据DOM树创建一个静态文件,即DOM树的一个快照
然后拿选择器和快照进行对比,符合选择器就放到合集中,知道整个快照对比完,再把合集的快照返回
绑定方式
直接通过HTML属性绑定
缺点
有可能响应函数还未被解析就被触发
事件名和回调1对1,修改繁琐
响应执行时,作用域为全局,不同浏览器解析规则不同,造成某些对象无法访问
使用dom属性绑定
冒泡阶段执行
缺点
只能绑定一个响应,后续赋值会被覆盖
IE attachEvent/detachEvent绑定/解绑
冒泡阶段执行
绑定多个时按按绑定顺序的逆序执行
addEventListener()/removeEventListener()绑定/解绑
第三个参数为对象时,passive属性为true时,会针对touchstart,touchend等特定事件通过开启两个线程,
一个执行浏览器默认行为,一个执行JS进行优化
事件优先级
浏览器在绑定事件而不是JS在绑定事件
1.html标签绑定事件会被dom属性事件覆盖
2.html标签优先执行,即使被属性事件覆盖,则执行被覆盖的属性事件
3.仅有监听事件和属性事件时,按绑定顺序执行,即事件对象的冒泡事件可能会在捕获事件之前执行
event对象属性
curentTarget:响应函数绑定的对象
target:发生事件的对象
1.如何使用 React 的鼠标事件系统
2.如何判断拖放开始和拖放结束
3.如何实现拖放元素的位置移动 (可分为两种,一种是直接拖着具体要被移动的dom移动;另外一种是具体dom留在原位,拖着具体dom的影子移动,确定位置后,再将具体的dom放过去)
4.拖放状态在组件中如何维护
鼠标移动可能会超出要移动的组件和他的父组件,除了在document上监听,还可以在一个全局透明遮罩层上监听MouseMove和MouseUp好处:拖放过程不会选中其他任何元素,防止点击到其他组件
其他情景思考:
每个条目高度不一致,如何确定移动位置?
条目所在的列表有折叠,存在滚动条,如何根据滚动条确定位置?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
97
98
99
100
import React, { Component } from "react";
require("./DndSample.css");
const list = [];
for (let i = 0; i < 10; i++) {
list.push(`Item ${i + 1}`);
}
const move = (arr, startIndex, toIndex) => {
arr = arr.slice();
arr.splice(toIndex, 0, arr.splice(startIndex, 1)[0]);
return arr;
};
const lineHeight = 42;
class DndSample extends Component {
constructor(props) {
super(props);
this.state.list = list;
}
state = {
dragging: false,
draggingIndex: -1,
startPageY: 0,
offsetPageY: 0,
};
handleMounseDown = (evt, index) => {
this.setState({
dragging: true,
startPageY: evt.pageY,
currentPageY: evt.pageY,
draggingIndex: index,
});
};
handleMouseUp = () => {
this.setState({ dragging: false, startPageY: 0, draggingIndex: -1 });
};
//如果往下滑,就一次把下一条数据交换位置,如果往上移动,就一次把上一条数据交换位置,
handleMouseMove = evt => {
let offset = evt.pageY - this.state.startPageY;
const draggingIndex = this.state.draggingIndex;
if (offset > lineHeight && draggingIndex < this.state.list.length - 1) {
// move down
offset -= lineHeight;
this.setState({
list: move(this.state.list, draggingIndex, draggingIndex + 1),
draggingIndex: draggingIndex + 1,
startPageY: this.state.startPageY + lineHeight,
});
} else if (offset < -lineHeight && draggingIndex > 0) {
// move up
offset += lineHeight;
this.setState({
list: move(this.state.list, draggingIndex, draggingIndex - 1),
draggingIndex: draggingIndex - 1,
startPageY: this.state.startPageY - lineHeight,
});
}
this.setState({ offsetPageY: offset });
};
getDraggingStyle(index) {
if (index !== this.state.draggingIndex) return {};
return {
backgroundColor: "#eee",
transform: `translate(10px, ${this.state.offsetPageY}px)`,
opacity: 0.5,
};
}
render() {
return (
<div className="dnd-sample">
<ul>
{this.state.list.map((text, i) => (
<li
key={text}
onMouseDown={evt => this.handleMounseDown(evt, i)}
style={this.getDraggingStyle(i)}
>
{text}
</li>
))}
</ul>
{this.state.dragging && ( //在一个遮罩层上监听MouseMove和MouseUp
<div
className="dnd-sample-mask"
onMouseMove={this.handleMouseMove}
onMouseUp={this.handleMouseUp}
/>
)}
</div>
);
}
}
export default DndSample;
1 | .dnd-sample ul { |
React 16.3 新引入的 API
可以将虚拟 DOM 映射到任何真实 DOM 节点
解决了漂浮层的问题,比如Dialog,Tooltip 等
主要思路使用ReactDOM.createPortal将自定义dialog利用id挂到根标签上,dialog样式(包括悬浮)自己需要通过css定义,弹窗的显示和隐藏通过state操作
1 | mport React from "react"; |
1 | .portal-sample { |
1 | import React from "react"; |
1.从0开始用 CSS实现
2.使用 CSS Grid 系统 (通过class实现,无需关心布局如何实现,同时可以适应不同屏幕的尺寸)
3.使用组件库,例如 antd(通过组件标签实现)
布局常见场景:侧边栏宽度可调整
1.手动实现拖放逻辑
2.使用 local storage 存储宽度位置
1 | <div className="app-layout1"> |
1 | <style> |
1 | <div className="app-layout2"> |
1 | .app-layout2 { |
1 | import React from "react"; |
1 | .app-layout-resize { |
表单各项配置的优化,可以通过对UI框架的API进行封装成高阶组件,然后在使用时,通过配置高阶组件的属性,将需要的配置项传递给UI框架API,减少代码冗余,配置清晰
提交后状态通过中间件返回promise判断接下来操作
2.
错误处理(可依靠UI框架进行判断)
动态表单元素(在传入高阶组件属性时采用函数返回值形式动态加载表单元素,在函数中处理要不要显示表单元素)
内容动态加载(在componentDidMount中进行数据请求,以及初始状态处理)
开发列表也要考虑的技术要点
1.如何翻页 (数据来自服务器还是客户端缓存)
2.如何进行内容搜索 (当前页面数据搜索还是服务器端数据搜索)
3.如何缓存数据
4.何时进行页面刷新
列表页数据通过store拿数据渲染,翻页,查找,刷新通过触发action然后从服务器拿到数据后,更新store,从而组件重新渲染
store 模型
listItems:当前页id集合数组[id1,id2]
byId:{id1:{id:id1,name:xxx},id2:{id:id2,name:xxx}}
keyword:关键字string
page:number 页数
fetchListPending:bool是否正在加载数据
FetchListError:OBJECT数据加载出错
listNeedReload:bool是否需要重新加载(做判断条件,例如编辑之后,设为true,在列表页拿到判断为true则重新获取列表数据)
URL设计
将页数和关键字当做路由参数传递在componentDidupdated中获取参数判断是否重新获取数据
通过store模型中的相关数据进行判断展示
页面数据来自多个请求
1.请求之间无依赖关系,可以并发进行
2.请求有依赖,需要依次进行 (promise)
3.请求完成之前,页面显示 Loading 状态
loading数据状态由当前数据推导确定
内容页和列表页的数据关系
1.简单业务:列表页数据包含内容页的数据 (注意页面刷新情况数据的获取)
2.复杂业务:内容页数据需要额外获取 (每次进来都重新获取)
3.内容页数据的缓存(将数据放在store中,在store中拿数据)
向导页面需要考虑的技术要点
1.使用 URL进行导航的好处 (可以直接刷新其中一个步骤)
将表单元素放在一个统一的form中,通过路由判断第几步,进而显示不同的表单元素,需要注意的是,切换下一步时,当前组件消失,配置的数据也会消失,需要将当前所填数据进行保存,以便在返回上一步时有数据
1.使用 ref 获取原生 DOM 节点引用
2.手动将组件状态更新到 DOM 节点
(对于数据驱动UI展示的情况,第一次渲染和之后更新需要手动操作DOM的过程,将数据注入过程隔离出来,单独处理成一个函数,再当react 部分操作导致需要重新渲染时,再调用,将react状态映射到第三方DOM中)
3.组件销毁时移除原生节点 DOM 事件
1.菜单导航只是改变 URL 状态
2.根据当前 URL 显示菜单的当前状态(使用navLink标签实现,注意react-router和redux一起使用时,url发生变化,组建要重新render时,必须要让组件绑定到router的store上,即让react-router也一起渲染)
1.了解常见的性能问题场景 (键盘输入,鼠标移动是否卡顿)
2.时刻注意代码的潜在性能问题 (何时拆分组件,有优化空间;能否高效更新,组件拆分是否够细,足够细的话,组件越接近纯函数,就越可能减少参与到diff操作的可能性,从而提高渲染速度)
3.注重可重构的代码 (代码耦合性低,几乎不依赖外界,也不被外界依赖,对于有重构可能性的代码保留重构空间,当其他优先级较高的性能问题解决后,再进行代码重构)
4.了解如何使用工具定位性能问题
1.什么是按需加载 (切换页面时才加载相应页面,而不是一开始就将所有页面加载进来)
2.使用 Webpack 的 import API
3.使用 react-loadable 库实现 React 异步加载
利用分包加载,在配置路由时实现,在总路由中删除,防止打包到总包中1
2
3
4
5import loadable from 'react-loadable'
const listPage = loadable({
loader:()=>import ('./listPage'),
loading:()=><div>loading...</div>
})
listPage页面在加载该页时,会自动加载listPage的独立打包文件
使用 reselect 避免重复计算
reselect 可以创建自动缓存的数据处理流程,通过保存计算结果,避免重复计算
时间分片
DOM操作的优先级低于浏览器原生行为,例如键盘和鼠标输入,从而保证操作的流畅。
(setstate导致的重新render等页面行为(滚动,输入)结束后再执行)
1.虚拟 DOM 的 diff 操作可以分片进行 (对操作进行序列化,然后进行合并或者优先级处理)
2.React 新 API: unstatble_deferredUpdates(低级别的setstate)
3.Chrome 新 API:requestIdleCallback(浏览器级别的api,告诉代码内存空闲(不再滚动或敲击键盘),可以进行一些之前优先级低的UI更新操作,时间分片基础)
渲染挂起
虚拟DOM节点可以等待某个异步操作的完成,并指定timeout后,才完成真正的渲染
(可以不再注意什么时候显示loading状态,取消loading状态,虚拟dom节点可以自己等待异步操作的完成,只要虚拟dom节点返回一个promise,渲染引擎就会等promise结束后再去render)
新内置组件:Timeout
unstatble_deferUpdate
1.使用 React DevTool 找到多余渲染
chrome 插件,可查看react应用的状态,帮助找到不必要的render,从而进行调优—> [highlight update]
2.使用 Chrome DevTool 定位性能瓶颈
什么样的组件花了多长时间做了什么样的渲染—>performance
背景
一个独立功能通常需要多个文件组成 (组件,reducer,action,路由等文件)
代码模板很复杂 (reducer,action需要代码模板,实际上填写的逻辑只是一小部分)
重构极为困难
(需要改动的地方很多)
项目复杂后很难理解和维护(无法直观的看到他们之间的关系)
Rekit:更好的代码导航
Rekit: 一键生成项目元素(它是一个IDE)
Rekit: 重构非常容易
Rekit: 可视化的项目架构
Rekit 是如何工作的?
1.以 feature 方式组织代码 (功能如果是必须的就放在已有模块底层实现,如果是原有功能增强的实现,则新拆分成功能模块)
2.拆分组件,action 和 reducer
3.拆分路由配置
通过代码自动生成保持一致性
1.文件夹结构一致性
2.文件名一致性
3.变量名一致性
4.代码逻辑的一致性
1.实现基础:React Router 的动态路由机制 (
不采用各个页面判断是否登录再redirect的方式跳转,路由配置(添加属性,那些可以在未登录下访问,或者哪些在未登录下不能访问)时就去动态监测当前用户是否登录,如果登录则全部路由生效;反之某些路由配置就不生效===>不生效处理,在解析router JSON数据时根据store中登录状态和路由配置的属性判断,未登录则跳转403,其子路由清空
)
2.区分受保护路由和公开路由
3.访问未授权路由时重定向到登录页面