WebAPI
获取元素
document. getElementById ( )
document. getElementsByTagName ( )
document. getElementsByClassName ( )
document. querySelector ( )
document. querySelectorAll ( )
document. documentElement
document. body
事件
window. onclick = ( ) => { }
window. onblur = ( ) => { }
window. onfocus = ( ) => { }
window. onmousedown = ( ) => { }
window. onmousemove = ( ) => { }
window. onmouseup = ( ) => { }
window. onmouseover = ( ) => { }
window. onmouseout = ( ) => { }
window. onmouseenter = ( ) => { }
window. onmouseleave = ( ) => { }
window. onkeypress = ( ) => { }
window. onkeydown = ( ) => { }
window. onkeyup = ( ) => { }
事件源的位置
clientX、clientY: 相对于浏览器窗口可视区域的X,Y坐标(窗口坐标)、 可视区域不包括工具栏和滚动条、IE事件和标准事件都定义了这2个属性。 pageX、pageY: 类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。 这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性。 offsetX、offsetY: 相对于事件源元素(target或srcElement)的X,Y坐标, 只有IE事件有这2个属性,标准事件没有对应的属性。 screenX、screenY: 相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性
操作元素
document. documentElement. innerHTML = ''
document. documentElement. innerText = ''
document. documentElement. style
document. documentElement. className
document. documentElement. getAttribute ( )
document. documentElement. setAttribute ( )
document. documentElement. removeAttribute ( )
元素节点
document. body. parentNode
document. body. childNodes
document. body. children
document. body. firstChild
document. body. lastChild
document. body. firstElementChild
document. body. lastElementChild
document. body. nextSibling
document. body. previousSibling
document. body. nextElementSibling
document. body. previousElementSibling
document. createElement ( )
document. body. appendChild ( )
document. body. insertBefore ( )
document. body. removeChild ( )
document. body. cloneNode ( )
元素属性
document. body. offsetLeft
document. body. offsetTop
document. body. offsetHeight
document. body. offsetWidth
document. body. clientHeight
document. body. clientWidth
document. body. clientLeft
document. body. clientTop
document. body. scrollHeight
document. body. scrollWidth
document. body. scrollLeft
document. body. scrollTop
const { x, y, left, top, bottom, right } = document. body. getBoundingClientRect ( )
BOM对象
window. onload = ( ) => { }
setTimeout ( ( ) => { } , 0 )
setInterval ( ( ) => { } , 0 )
const {
hash,
host,
href,
pathname,
port,
protocol,
search,
assign,
reload,
replace,
} = location
localStorage. setItem ( 'localStorage' , 1 )
localStorage. getItem ( 'localStorage' )
localStorage. removeItem ( 'localStorage' )
localStorage. clear ( )
sessionStorage. setItem ( 'sessionStorage' , 1 )
sessionStorage. getItem ( 'sessionStorage' )
sessionStorage. removeItem ( 'sessionStorage' )
sessionStorage. clear ( )
操作元素综合示例(键盘移动活动表格)
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< meta http-equiv = " X-UA-Compatible" content = " IE=edge" >
< meta name = " viewport" content = " width=device-width, initial-scale=1.0" >
< title> Document</ title>
< style>
table {
font-size : 14px;
border-collapse : collapse;
width : 100%;
table-layout : fixed;
}
table td {
border : 1px solid #e1e1e1;
padding : 0;
height : 30px;
text-align : center;
}
table td.current {
background : #1890ff;
}
</ style>
</ head>
< body>
< div>
< table>
< tbody>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td class = " current" > </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
< tr>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
< td> </ td>
</ tr>
</ tbody>
</ table>
</ div>
< script>
document. onkeydown = event => {
if ( ! event) return
var code = event. keyCode || ''
if ( ! { '37' : 1 , '38' : 1 , '39' : 1 , '40' : 1 } [ code] ) return
var tbody = document. querySelector ( 'tbody' )
var current = document. querySelector ( '.current' )
current. className = ''
if ( code == 37 ) {
if ( current. cellIndex == 0 ) {
current = current. parentNode. children[ 8 ]
current. className = 'current'
} else {
current. previousElementSibling. className = 'current'
}
} else if ( code == 38 ) {
if ( current. parentNode. rowIndex == 0 ) {
current = tbody. children[ 8 ] . children[ current. cellIndex]
current. className = 'current'
} else {
current = current. parentNode. previousElementSibling. children[ current. cellIndex]
current. className = 'current'
}
} else if ( code == 39 ) {
if ( current. cellIndex == 8 ) {
current = current. parentNode. children[ 0 ]
current. className = 'current'
} else {
current. nextElementSibling. className = 'current'
}
} else if ( code == 40 ) {
if ( current. parentNode. rowIndex == 8 ) {
current = tbody. children[ 0 ] . children[ current. cellIndex]
current. className = 'current'
} else {
current = current. parentNode. nextElementSibling. children[ current. cellIndex]
current. className = 'current'
}
}
}
</ script>
</ body>
</ html>
执行上下文和执行栈
执行上下文
1、JS代码都是在执行上下文中执行的
2、执行上下文: 指当前执行环境中的变量、函数声明、作用域链、this等信息
3、执行上下文分为全局、函数、Eval执行上下文
1)全局执行上下文(浏览器环境下,为全局的 window 对象)
2)函数执行上下文,每当一个函数被调用时, 都会为该函数创建一个新的上下文
3)Eval 函数执行上下文,如eval("1 + 2")
4、对于每个执行上下文,都有三个重要属性:变量对象、作用域链(Scope chain)、this
执行上下文的特点
1、单线程,只在主线程上运行
2、同步执行,从上向下按顺序执行
3、全局上下文只有一个,也就是window对象
4、函数每调用一次就会产生一个新的执行上下文环境
执行上下文的生命周期
1、创建阶段:生成变量对象、建立作用域链、确定this指向
2、执行阶段: 变量赋值、函数引用、执行其他代码
1.创建变量对象:
1) 变量
2) 函数及函数的参数
3) 全局: window
4) 局部: 局部变量
2.确认this的指向
1) 全局: this -- -> window
2) 局部: this -- -> 调用其的对象
3.创建作用域链
父级作用域链 + 当前的变量对象
4.扩展:
ECobj = {
变量对象:{变量,函数,函数的形参}
scopeChain:父级作用域链+当前的变量对象,
this: {window | 调用其的对象}
}
执行栈
执行栈是一种先进后出的数据结构,用来存储代码运行的所有执行上下文
1)当 JS 引擎第一次遇到js脚本时,会创建一个全局的执行上下文并且压入当前执行栈
2)每当JS 引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部
3)当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文
4)一旦所有代码执行完毕,JS 引擎从当前栈中移除全局执行上下文
作用域
作用域:可访问变量的集合,最大的作用就是隔离变量,不同的作用域下同名变量不会有冲突
作用域类型:全局作用域、函数作用域、块级作用域(ES6)
全局作用域:全局上下文的变量
函数作用域:是指声明在函数内部的变量,函数的作用域在函数定义的时候就决定了
块级作用域:块作用域由{ }包括,if和for语句里面的{ }也属于块作用域,在块级作用域中,可通过let和const声明变量,该变量在指定块的作用域外无法被访问
var let const的区别
1、var定义的变量,没有块的概念,可以跨块访问, 可以变量提升
2、let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明
3、const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改,无变量提升,不可以重复声明
作用域链
当查找变量的时候,首先会先从当前上下文的变量对象(作用域)中查找,
如果没有找到,就会从父级的执行上下文的变量对象中查找,
如果还没有找到,一直找到全局上下文的变量对象,也就是全局对象。
这样由多个执行上下文的变量对象构成的链表就叫做作用域链
作用域和值类型引用类型的值传递
var num1 = 55
var num2 = 66
function f1 ( num, num1 ) {
num = 100
num1 = 100
num2 = 100
console. log ( num)
console. log ( num1)
console. log ( num2)
}
f1 ( num1, num2)
console. log ( num1)
console. log ( num2)
console. log ( num) ;
function Person ( name, age, salary ) {
this . name = name
this . age = age
this . salary = salary
}
function f2 ( person ) {
person. name = 'ls'
person = new Person ( 'aa' , 18 , 10 )
}
var p = new Person ( 'zs' , 18 , 1000 )
console. log ( p. name)
f2 ( p)
console. log ( p. name)
变量提升
js引擎在代码正式执行之前会做一个预处理的工作:
1.收集变量
2.收集函数
依据:
var :将var后边的变量定义但是不赋值 var username = undefined;
function(){} 提前定义该函数
变量的提升是变量名的提升,函数提升是整体的提升
变量与函数同名,提升以函数为准
console. log ( username)
var username = 'kobe'
console. log ( username)
fun ( )
function fun ( ) {
console. log ( 'fun()' )
}
function Foo ( ) {
getName = function ( ) {
console. log ( 1 )
}
return this
}
Foo. getName = function ( ) {
console. log ( 2 )
}
Foo . prototype. getName = function ( ) {
console. log ( 3 )
}
var getName = function ( ) {
console. log ( 4 )
}
function getName ( ) {
console. log ( 5 )
}
Foo. getName ( )
getName ( )
Foo ( ) . getName ( )
getName ( )
new Foo. getName ( )
new Foo ( ) . getName ( )
new new Foo ( ) . getName ( )
function A ( ) {
console. log ( 1 )
}
function Fn ( ) {
A = function ( ) {
console. log ( 2 )
}
return this
}
Fn. A = A
Fn . prototype = {
A : ( ) => {
console. log ( 3 )
} ,
}
A ( )
Fn. A ( )
Fn ( ) . A ( )
new Fn. A ( )
new Fn ( ) . A ( )
new new Fn ( ) . A ( )
}
this的指向问题
this的绑定方式
1)默认绑定(非严格模式下this指向全局对象,严格模式下函数内的this指向undefined)
2)隐式绑定(当函数引用有上下文对象时, 如 obj.foo()的调用方式, foo内的this指向obj,谁调用指向谁)
3)显示绑定(通过call或者apply方法直接指定this的绑定对象, 如foo.call(obj))
4)new构造函数绑定,this指向新生成的对象
5)箭头函数,this指向的是定义该函数时,外层环境中的this,箭头函数的this在定义时就决定了,不能改变
手写 new
function New ( fn, ... args ) {
const instance = object. create ( fn. prototype)
const res = fn . apply ( instance, args)
return typeof res === 'object' || typeof res === 'function' ? res : instance
}
function Person ( name ) {
this . name = name
}
Person . prototype. eat = function ( ) {
console. log ( 'Eatting' )
}
var lindaidai = New ( Person, 'LinDaiDai' )
console. log ( lindaidai, 'New' )
lindaidai. eat ( )
手写call、apply、bind
Function . prototype. Call = function ( context, ... args ) {
if ( ! context) context = window
const f = Symbol ( )
context[ f] = this
const res = context[ f] ( ... args)
delete context[ f]
return res
}
Function . prototype. Apply = function ( context, ... args ) {
if ( ! context) context = window
const f = Symbol ( )
context[ f] = this
const res = context[ f] ( args)
delete context[ f]
return res
}
Function . prototype. Bind = function ( context, ... args ) {
if ( ! context) context = window
const f = Symbol ( )
context[ f] = this
return function ( ... args1) {
const res = context[ f] ( ... args, ... agrs1)
delete context[ f]
return res
}
}
var obj = {
name: 'objName' ,
}
var name = 'globalName'
function consoleInfo ( sex, weight ) {
console. log ( this . name, sex, weight, 'this指向 call apply bind' )
}
consoleInfo ( 'man' , 100 )
consoleInfo. Call ( obj, 'man' , 100 )
consoleInfo. Call ( obj, 'woman' , 120 )
consoleInfo. Apply ( obj, [ 'man' , 100 ] )
consoleInfo. Apply ( obj, [ 'woman' , 120 ] )
consoleInfo. Bind ( obj, 'man' , 100 ) ( )
consoleInfo. Bind ( obj, 'woman' , 120 ) ( )
相关题目
var a = 10
function foo ( ) {
console. log ( this . a)
}
foo ( )
'use strict'
var a = 10
function foo ( ) {
console. log ( 'this1' , this )
console. log ( window. a)
console. log ( this . a)
}
console. log ( window. foo)
console. log ( 'this2' , this )
foo ( )
let a = 10
const b = 20
function foo ( ) {
console. log ( this . a)
console. log ( this . b)
}
foo ( )
console. log ( window. a)
let a = 10
const b = 20
function foo ( ) {
console. log ( this . a)
console. log ( this . b)
}
foo ( )
console. log ( window. a)
var a = 1
function foo ( ) {
var a = 2
console. log ( this )
console. log ( this . a)
}
foo ( )
var a = 1
function foo ( ) {
var a = 2
function inner ( ) {
console. log ( this . a)
}
inner ( )
}
foo ( )
function foo ( ) {
console. log ( this . a)
}
var obj = { a: 1 , foo }
var a = 2
foo ( )
obj. foo ( )
function foo ( ) {
console. log ( this . a)
}
var obj = { a: 1 , foo }
var a = 2
var foo2 = obj. foo
obj. foo ( )
foo2 ( )
function foo ( ) {
console. log ( this . a)
}
var obj = { a: 1 , foo }
var a = 2
var foo2 = obj. foo
var obj2 = { a: 3 , foo2: obj. foo }
obj. foo ( )
foo2 ( )
obj2. foo2 ( )
function foo ( ) {
console. log ( this . a)
}
function doFoo ( fn ) {
console. log ( this )
fn ( )
}
var obj = { a: 1 , foo }
var a = 2
doFoo ( obj. foo)
function foo ( ) {
console. log ( this . a)
}
function doFoo ( fn ) {
console. log ( this )
fn ( )
}
var obj = { a: 1 , foo }
var a = 2
var obj2 = { a: 3 , doFoo }
obj2. doFoo ( obj. foo)
'use strict'
function foo ( ) {
console. log ( this . a)
}
function doFoo ( fn ) {
console. log ( this )
fn ( )
}
var obj = { a: 1 , foo }
var a = 2
var obj2 = { a: 3 , doFoo }
obj2. doFoo ( obj. foo)
function foo ( ) {
console. log ( this . a)
}
var obj = { a: 1 }
var a = 2
foo ( )
foo . call ( obj)
foo . apply ( obj)
foo . bind ( obj) ( )
var obj1 = {
a: 1 ,
}
var obj2 = {
a: 2 ,
foo1 : function ( ) {
console. log ( this . a)
} ,
foo2 : function ( ) {
setTimeout ( function ( ) {
console. log ( this )
console. log ( this . a)
} , 0 )
} ,
}
var a = 3
obj2. foo1 ( )
obj2. foo2 ( )
var obj1 = {
a: 1 ,
}
var obj2 = {
a: 2 ,
foo1 : function ( ) {
console. log ( this . a)
} ,
foo2 : function ( ) {
setTimeout (
function ( ) {
console. log ( this )
console. log ( this . a)
} . call ( obj1) ,
0
)
} ,
}
var a = 3
obj2. foo1 ( )
obj2. foo2 ( )
var obj1 = {
a: 1 ,
}
var obj2 = {
a: 2 ,
foo1 : function ( ) {
console. log ( this . a)
} ,
foo2 : function ( ) {
function inner ( ) {
console. log ( this )
console. log ( this . a)
}
inner ( )
} ,
}
var a = 3
obj2. foo1 ( )
obj2. foo2 ( )
function foo ( ) {
console. log ( this . a)
}
var obj = { a: 1 }
var a = 2
foo ( )
foo . call ( obj)
foo ( ) . call ( obj)
function foo ( ) {
console. log ( this . a)
return function ( ) {
console. log ( this . a)
}
}
var obj = { a: 1 }
var a = 2
foo ( )
foo . call ( obj)
foo ( ) . call ( obj)
function foo ( ) {
console. log ( this . a)
return function ( ) {
console. log ( this . a)
}
}
var obj = { a: 1 }
var a = 2
foo ( )
foo . bind ( obj)
foo ( ) . bind ( obj)
function foo ( ) {
console. log ( this . a)
return function ( ) {
console. log ( this . a)
}
}
var obj = { a: 1 }
var a = 2
foo . call ( obj) ( )
var obj = {
a: 'obj' ,
foo : function ( ) {
console. log ( 'foo:' , this . a)
return function ( ) {
console. log ( 'inner:' , this . a)
}
} ,
}
var a = 'window'
var obj2 = { a: 'obj2' }
obj. foo ( ) ( )
obj. foo . call ( obj2) ( )
obj. foo ( ) . call ( obj2)
var obj = {
a: 1 ,
foo : function ( b ) {
b = b || this . a
return function ( c ) {
console. log ( this . a + b + c)
}
} ,
}
var a = 2
var obj2 = { a: 3 }
obj. foo ( a) . call ( obj2, 1 )
obj. foo . call ( obj2) ( 1 )
function foo1 ( ) {
console. log ( this . a)
}
var a = 1
var obj = {
a: 2 ,
}
var foo2 = function ( ) {
foo1 . call ( obj)
}
foo2 ( )
foo2 . call ( window)
function foo1 ( b ) {
console. log ( ` ${ this . a} + ${ b} ` )
return this . a + b
}
var a = 1
var obj = {
a: 2 ,
}
var foo2 = function ( ) {
return foo1 . call ( obj, ... arguments)
}
var num = foo2 ( 3 )
console. log ( num)
function foo ( item ) {
console. log ( item, this . a)
}
var obj = {
a: 'obj' ,
}
var a = 'window'
var arr = [ 1 , 2 , 3 ]
arr. forEach ( foo, obj)
arr. map ( foo, obj)
arr. filter ( function ( i ) {
console. log ( i, this . a)
return i > 2
} , obj)
function Person ( name ) {
this . name = name
}
var name = 'window'
var person1 = new Person ( 'LinDaiDai' )
console. log ( person1. name)
function Person ( name ) {
this . name = name
this . foo1 = function ( ) {
console. log ( this . name)
}
this . foo2 = function ( ) {
return function ( ) {
console. log ( this . name)
}
}
}
var person1 = new Person ( 'person1' )
person1. foo1 ( )
person1. foo2 ( ) ( )
var name = 'window'
function Person ( name ) {
this . name = name
this . foo = function ( ) {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
}
}
var person2 = {
name: 'person2' ,
foo : function ( ) {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
} ,
}
var person1 = new Person ( 'person1' )
person1. foo ( ) ( )
person2. foo ( ) ( )
var name = 'window'
function Person ( name ) {
this . name = name
this . foo = function ( ) {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
}
}
var person1 = new Person ( 'person1' )
var person2 = new Person ( 'person2' )
person1. foo . call ( person2) ( )
person1. foo ( ) . call ( person2)
var obj = {
name: 'obj' ,
foo1 : ( ) => {
console. log ( this . name)
} ,
foo2 : function ( ) {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
} ,
}
var name = 'window'
obj. foo1 ( )
obj. foo2 ( ) ( )
var name = 'window'
var obj1 = {
name: 'obj1' ,
foo : function ( ) {
console. log ( this . name)
} ,
}
var obj2 = {
name: 'obj2' ,
foo : ( ) => {
console. log ( this . name)
} ,
}
obj1. foo ( )
obj2. foo ( )
var name = 'window'
var obj1 = {
name: 'obj1' ,
foo : function ( ) {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
} ,
}
var obj2 = {
name: 'obj2' ,
foo : function ( ) {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
} ,
}
var obj3 = {
name: 'obj3' ,
foo : ( ) => {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
} ,
}
var obj4 = {
name: 'obj4' ,
foo : ( ) => {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
} ,
}
obj1. foo ( ) ( )
obj2. foo ( ) ( )
obj3. foo ( ) ( )
obj4. foo ( ) ( )
var name = 'window'
function Person ( name ) {
this . name = name
this . foo1 = function ( ) {
console. log ( this . name)
}
this . foo2 = ( ) => {
console. log ( this . name)
}
}
var person2 = {
name: 'person2' ,
foo2 : ( ) => {
console. log ( this . name)
} ,
}
var person1 = new Person ( 'person1' )
person1. foo1 ( )
person1. foo2 ( )
person2. foo2 ( )
var name = 'window'
function Person ( name ) {
this . name = name
this . foo1 = function ( ) {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
}
this . foo2 = function ( ) {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
}
this . foo3 = ( ) => {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
}
this . foo4 = ( ) => {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
}
}
var person1 = new Person ( 'person1' )
person1. foo1 ( ) ( )
person1. foo2 ( ) ( )
person1. foo3 ( ) ( )
person1. foo4 ( ) ( )
var name = 'window'
var obj1 = {
name: 'obj1' ,
foo1 : function ( ) {
console. log ( this . name)
return ( ) => {
console. log ( this . name)
}
} ,
foo2 : ( ) => {
console. log ( this . name)
return function ( ) {
console. log ( this . name)
}
} ,
}
var obj2 = {
name: 'obj2' ,
}
obj1. foo1 . call ( obj2) ( )
obj1. foo1 ( ) . call ( obj2)
obj1. foo2 . call ( obj2) ( )
obj1. foo2 ( ) . call ( obj2)
var name = 'window'
var person1 = {
name: 'person1' ,
foo1 : function ( ) {
console. log ( this . name)
} ,
foo2 : ( ) => console. log ( this . name) ,
foo3 : function ( ) {
return function ( ) {
console. log ( this . name)
}
} ,
foo4 : function ( ) {
return ( ) => {
console. log ( this . name)
}
} ,
}
var person2 = { name: 'person2' }
person1. foo1 ( )
person1. foo1 . call ( person2)
person1. foo2 ( )
person1. foo2 . call ( person2)
person1. foo3 ( ) ( )
person1. foo3 . call ( person2) ( )
person1. foo3 ( ) . call ( person2)
person1. foo4 ( ) ( )
person1. foo4 . call ( person2) ( )
person1. foo4 ( ) . call ( person2)
var name = 'window'
function Person ( name ) {
this . name = name
this . foo1 = function ( ) {
console. log ( this . name)
}
this . foo2 = ( ) => console. log ( this . name)
this . foo3 = function ( ) {
return function ( ) {
console. log ( this . name)
}
}
this . foo4 = function ( ) {
return ( ) => {
console. log ( this . name)
}
}
}
var person1 = new Person ( 'person1' )
var person2 = new Person ( 'person2' )
person1. foo1 ( )
person1. foo1 . call ( person2)
person1. foo2 ( )
person1. foo2 . call ( person2)
person1. foo3 ( ) ( )
person1. foo3 . call ( person2) ( )
person1. foo3 ( ) . call ( person2)
person1. foo4 ( ) ( )
person1. foo4 . call ( person2) ( )
person1. foo4 ( ) . call ( person2)
var name = 'window'
function Person ( name ) {
this . name = name
this . obj = {
name: 'obj' ,
foo1 : function ( ) {
return function ( ) {
console. log ( this . name)
}
} ,
foo2 : function ( ) {
return ( ) => {
console. log ( this . name)
}
} ,
}
}
var person1 = new Person ( 'person1' )
var person2 = new Person ( 'person2' )
person1. obj. foo1 ( ) ( )
person1. obj. foo1 . call ( person2) ( )
person1. obj. foo1 ( ) . call ( person2)
person1. obj. foo2 ( ) ( )
person1. obj. foo2 . call ( person2) ( )
person1. obj. foo2 ( ) . call ( person2)
function foo ( ) {
console. log ( this . a)
}
var a = 2
; ( function ( ) {
'use strict'
foo ( )
} ) ( )
闭包
什么是闭包?
1.密闭的容器.类似于set,map
2.闭包是一个对象.存放数据的格式:key:value
形成的条件:
1.函数嵌套
2.内部函数引用外部函数的局部变量
闭包的优点:
1.延长外部函数局部变量的生命周期
闭包的缺点:
容易造成内存泄漏
注意点:
1.合理使用闭包
2.用完闭包要及时销毁
防抖
function debounce ( fn, wait ) {
let timer = null
return function ( ) {
const _this = this
const args = arguments
if ( timer) clearTimeout ( timer)
timer = setTimeout ( function ( ) {
fn . apply ( _this, args)
} , wait)
}
}
节流
function throttle ( fn, delay ) {
let curTime = Date. now ( )
return function ( ) {
let nowTime = Date. now ( )
if ( nowTime - curTime >= delay) {
curTime = Date. now ( )
fn . apply ( this , arguments)
}
}
}
function throttleSetTimeout ( fn, delay ) {
let timer = null
return function ( ) {
const _this = this
const args = arguments
if ( ! timer) {
timer = setTimeout ( function ( ) {
timer = null
fn . apply ( _this, args)
} , delay)
}
}
}
相关题目
function fun ( ) {
var count = 1
function fun2 ( ) {
console. log ( count)
}
fun2 ( )
}
fun ( )
function fun ( ) {
var count = 1
return function ( ) {
count++
console. log ( count)
}
}
var fun2 = fun ( )
fun2 ( )
fun2 ( )
function fun ( n, o ) {
console. log ( o)
return {
fun : function ( m ) {
return fun ( m, n)
} ,
}
}
var a = fun ( 0 )
a. fun ( 1 )
a. fun ( 2 )
a. fun ( 3 )
var b = fun ( 0 ) . fun ( 1 ) . fun ( 2 ) . fun ( 3 )
var c = fun ( 0 ) . fun ( 1 )
c. fun ( 2 )
c. fun ( 3 )
var test = ( function ( i ) {
return function ( ) {
alert ( ( i *= 2 ) )
}
} ) ( 2 )
test ( 5 )
var a = 0 ,
b = 0
function A ( a ) {
A = function ( b ) {
alert ( a + b++ )
}
alert ( a++ )
}
A ( 1 )
A ( 2 )
var x = 2
var y = {
x: 3 ,
z: ( function ( x ) {
this . x *= x
x += 2
return function ( n ) {
this . x *= n
x += 3
console. log ( x)
}
} ) ( x) ,
}
var m = y. z
m ( 4 )
y. z ( 5 )
console. log ( x, y. x)
for ( var i = 0 ; i < 5 ; i++ ) {
setTimeout ( function ( ) {
console. log ( new Date ( ) , i)
} , 1000 )
}
console. log ( new Date ( ) , i)
for ( var i = 0 ; i < 5 ; i++ ) {
; ( function ( j ) {
setTimeout ( function ( ) {
console. log ( new Date ( ) , j)
} , 1000 )
} ) ( i)
}
console. log ( new Date ( ) , i)
for ( var i = 0 ; i < 5 ; i++ ) {
setTimeout (
function ( j ) {
console. log ( new Date ( ) , j)
} ,
1000 ,
i
)
}
console. log ( new Date ( ) , i)
var output = function ( i ) {
setTimeout ( function ( ) {
console. log ( new Date ( ) , i)
} , 1000 )
}
for ( var i = 0 ; i < 5 ; i++ ) {
output ( i)
}
console. log ( new Date ( ) , i)
const tasks = [ ]
const output = ( i ) =>
new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
console. log ( new Date ( ) , i)
resolve ( )
} , 1000 * i)
} )
for ( var i = 0 ; i < 5 ; i++ ) {
tasks. push ( output ( i) )
}
Promise. all ( tasks) . then ( ( ) => {
setTimeout ( ( ) => {
console. log ( new Date ( ) , i)
} , 1000 )
} )
const sleep = ( timeountMS ) =>
new Promise ( ( resolve ) => {
setTimeout ( resolve, timeountMS)
} )
; ( async ( ) => {
for ( var i = 0 ; i < 5 ; i++ ) {
if ( i > 0 ) {
await sleep ( 1000 )
}
console. log ( new Date ( ) , i)
}
await sleep ( 1000 )
console. log ( new Date ( ) , i)
} ) ( )
原型和原型链
原型和原型链
原型的作用:原型被定义为给其它对象提供共享属性的对象,函数的实例可以共享原型上的属性和方法
原型链:
它的作用就是当你在访问一个对象上属性的时候,
如果该对象内部不存在这个属性,那么就会去它__proto__属性所指向的对象(原型对象)上查找。
如果原型对象依旧不存在这个属性,那么就会去其原型的__proto__属性所指向的原型对象上去查找。
以此类推,直到找到null,而这个查找的线路,也就构成了我们常说的原型链
原型链和作用域的区别: 原型链是查找对象上的属性,作用域链是查找当前上下文中的变量
proto、prototype、constructor属性介绍
1)js中对象分为两种,普通对象和函数对象
2)__proto__和constructor是对象独有的。
prototype属性是函数独有的,它的作用是包含可以给特定类型的所有实例提供共享的属性和方法;
但是在 JS 中,函数也是对象,所以函数也拥有__proto__和 constructor属性
3)constructor属性是对象所独有的,它是一个对象指向一个函数,这个函数就是该对象的构造函数
构造函数.prototype.constructor === 该构造函数本身
4)一个对象的__proto__指向其构造函数的prototype
函数创建的对象.__proto__ === 该函数.prototype
原型链完整关系
function Foo ( ) { }
const foo = new Foo ( )
手写instanceof
function Instanceof ( fn, context ) {
const proto = context. __proto__
if ( proto) {
if ( proto === fn. prototype) {
return true
} else {
return Instanceof ( fn, proto)
}
} else {
return false
}
}
console. log ( Instanceof ( Array, [ ] ) )
继承
function SuperClass ( ) {
this . name = 'Super'
this . info = {
child: 'Sub' ,
}
}
function SubClass ( ) { }
SubClass . prototype = new SuperClass ( )
const sub1 = new SubClass ( )
const sub2 = new SubClass ( )
sub1. name = 'sub1'
console. log ( sub1. name, sub2. name, '原型链继承 普通类型属性' )
sub1. info. child = 'sub1'
console. log ( sub1. info. child, sub2. info. child, '原型链继承 引用类型属性' )
function SuperClass ( ) {
this . name = 'Super'
this . info = {
child: 'Sub' ,
}
}
function SubClass ( ) {
SuperClass . call ( this )
}
SuperClass . prototype. super = 'prototype'
const sub1 = new SubClass ( )
const sub2 = new SubClass ( )
sub1. name = 'sub1'
console. log ( sub1. name, sub2. name, '盗用构造函数继承 普通类型属性' )
sub1. info. child = 'sub1'
console. log ( sub1. info. child, sub2. info. child, '盗用构造函数继承 引用类型属性' )
console. log ( sub1. super, '盗用构造函数继承 访问父类的原型' )
function SuperClass ( ) {
this . name = 'Super'
this . info = {
child: 'Sub' ,
}
console. log ( '组合式继承 父类执行了' )
}
function SubClass ( ) {
SuperClass . call ( this )
}
SubClass . prototype = new SuperClass ( )
SuperClass . prototype. super = 'prototype'
const sub1 = new SubClass ( )
const sub2 = new SubClass ( )
sub1. name = 'sub1'
console. log ( sub1. name, sub2. name, '组合式继承 普通类型属性' )
sub1. info. child = 'sub1'
console. log ( sub1. info. child, sub2. info. child, '组合式继承 引用类型属性' )
console. log ( sub1. super, '组合式继承 访问父类的原型' )
function createObject ( o ) {
function F ( ) { }
F . prototype = o
return new F ( )
}
function SuperClass ( ) {
this . name = 'Super'
this . info = {
child: 'Sub' ,
}
console. log ( '寄生式继承 父类执行了' )
}
SuperClass . prototype. super = 'prototype'
const superClass = new SuperClass ( )
function createObject ( obj ) {
let o = inheritObject ( obj)
o. childName = 'childName'
return o
}
const sub1 = createObject ( superClass)
const sub2 = createObject ( superClass)
console. log ( sub1. info. child, '寄生式继承 sub1.info.child before' )
sub1. info. child = 'child'
console. log ( sub1. info. child, '寄生式继承 sub1.info.child after' )
console. log ( sub2. info. child)
console. log ( sub1. super, '寄生式继承 sub1.super' )
console. log ( sub1. childName, '寄生式继承 sub1 childName' )
function SuperClass ( ) {
this . name = 'Super'
this . info = {
child: 'Sub' ,
}
console. log ( '寄生组合式继承 父类执行了' )
}
function SubClass ( ) {
SuperClass . call ( this )
}
SuperClass . prototype. super = 'prototype'
function inheritProtype ( SubClass, SuperClass ) {
let p = inheritObject ( SuperClass . prototype)
SubClass . prototype = p
p. constructor = SubClass
}
inheritProtype ( SubClass, SuperClass)
const sub1 = new SubClass ( )
const sub2 = new SubClass ( )
console. log ( sub1. info. child, '寄生组合式继承 sub1.info.child before' )
sub1. info. child = 'child'
console. log ( sub1. info. child, '寄生组合式继承 sub1.info.child after' )
console. log ( sub2. info. child)
console. log ( sub1. super, '寄生组合式继承 sub1.super' )
ES6 类
1) Class 类可以看作是构造函数的语法糖
2) Class 类中定义的方法,都是定义在该构造函数的原型上
3)使用static关键字,作为静态方法(静态方法,只能通过类调用,实例不能调用)
4)extents 关键字实际是寄生组合继承
了避免与访问器属性冲突,在构造函数中使用了一个带有下划线前缀的私有属性_myProperty。这是一种常见的命名约定,用于表示该属性应该被视为私有的,以防止直接访问
function Foo ( ) {
getName = function ( ) {
console. log ( 1 )
}
return this
}
Foo. getName = function ( ) {
console. log ( 2 )
}
Foo . prototype. getName = function ( ) {
console. log ( 3 )
}
var getName = function ( ) {
console. log ( 4 )
}
function getName ( ) {
console. log ( 5 )
}
Foo. getName ( )
getName ( )
getName ( )
new Foo. getName ( )
new Foo ( ) . getName ( )
new new Foo ( ) . getName ( )
Promise
手写promise
class MyPromise {
constructor ( execute ) {
this . state = 'pending'
this . data = undefined
this . error = undefined
this . resolveTask = [ ]
this . rejectTask = [ ]
try {
execute ( this . resolve . bind ( this ) , this . reject . bind ( this ) )
} catch ( e) {
this . reject ( e)
}
}
resolve = ( value ) => {
if ( this . state !== 'pending' ) return
this . state = 'fulfilled'
this . data = value
this . resolveTask. forEach ( cb => cb ( ) )
}
reject = ( error ) => {
if ( this . state !== 'pending' ) return
this . state = 'rejected'
this . error= error
this . rejectTask . forEach ( cb => cb ( ) )
}
then = ( onResolve, onReject ) => {
onResolve = typeof onResolve === 'function' ? onResolve : value => value
onReject = typeof onReject === 'function' ? onReject : ( error ) => throw error
return new MyPromise ( ( resolve, reject ) => {
this . resolveTask. push ( ( ) => {
const res = onResolve ( this . data)
if ( res instanceof MyPromise ) {
res. then ( resolve, reject)
} else {
resolve ( res)
}
} )
this . rejectTask. push ( ( ) => {
const res = onReject ( this . error)
if ( res instanceof MyPromise ) {
res. then ( resolve, reject)
} else {
reject ( res)
}
} )
} )
}
catch = ( onReject ) => {
return this . then ( undefined , onReject)
}
static resolve = ( value ) => {
return new MyPromise ( ( resolve, reject ) => {
if ( value instanceof MyPromise ) {
value. then ( resolve, reject)
} else {
resolve ( value)
}
} )
}
static reject = ( error ) => {
return new MyPromise ( ( resolve, reject ) => {
if ( value instanceof MyPromise ) {
error. then ( resolve, reject)
} else {
reject ( error)
}
} )
}
static race = ( promises ) => {
return new MyPromise ( ( resolve, reject ) => {
for ( let i = 0 ; i < promises. length; i++ ) {
MyPromise. resolve ( promises[ i] )
. then ( value => {
resolve ( value)
} , error => {
reject ( error)
} )
}
} )
}
static all = ( promises ) => {
const result = [ ]
let index = 0
return new MyPromise ( ( resolve, reject ) => {
for ( let i = 0 ; i < promises. length; i++ ) {
MyPromise. resolve ( promises[ i] )
. then ( value => {
result[ i] = value
index++
if ( index === promises. length - 1 ) {
resolve ( resolve ( value) )
}
} , error => {
reject ( error)
} )
}
} )
}
static retry ( fn, delay, times ) {
return new MyPromise ( ( resolve, reject ) => {
function func ( ) {
MyPromise. resolve ( fn ( ) )
. then ( ( res ) => {
resolve ( res)
} )
. catch ( ( err ) => {
if ( times !== 0 ) {
setTimeout ( func, delay)
times--
} else {
reject ( err)
}
} )
}
func ( )
} )
}
}
new MyPromise ( ( resolve, reject ) => {
setTimeout ( ( ) => {
resolve ( 1 )
} , 500 )
} )
. then ( ( res ) => {
console. log ( res)
return new MyPromise ( ( resolve ) => {
setTimeout ( ( ) => {
resolve ( 2 )
} , 1000 )
} )
} )
. then ( ( data ) => {
console. log ( data)
} )
async await generator
async 是 generator 的语法糖,返回一个Promise对象
await 只能写在 async 函数中,作用就是获取Promise中返回的reslove或者reject值
generator函数跟普通函数在写法上的区别就是,多了一个星号*
只有在generator函数中才能使用yield,相当于generator函数执行的中途暂停点
generator函数是不会自动执行的,每一次调用它的next方法,会停留在下一个yield的位置
generatorToAsync
function generatorToAsync ( generatorFunc ) {
return function ( ... args) {
const gen = generatorFunc . apply ( this , args)
return new Promise ( ( resolve, reject ) => {
function step ( key, arg ) {
let generatorResult
try {
generatorResult = gen[ key] ( arg)
} catch ( e) {
return reject ( e)
}
const { value, done } = generatorResult
if ( done) {
return resolve ( value)
} else {
Promise. resolve ( value)
. then ( res => {
step ( 'next' , res)
} , err => {
step ( 'throw' , err)
} )
}
}
step ( 'next' )
} )
}
}
const getData = ( ) =>
new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( 'data' ) , 1000 ) )
const test = generatorToAsync ( function * testG ( ) {
const data = yield getData ( )
console. log ( 'data1: ' , data)
const data2 = yield getData ( )
console. log ( 'data2: ' , data2)
return 'success'
} )
test ( ) . then ( ( res ) => console. log ( res) )
深拷贝
let obj = {
a: 100 ,
b: [ 10 , 20 , 30 ] ,
c: {
x: 10 ,
} ,
d: / ^\d+$ / ,
}
function deepClone ( obj ) {
if ( obj === null ) return obj
if ( typeof obj !== 'object' ) return obj
if ( obj instanceof Function ) return obj
if ( obj instanceof RegExp ) {
return new RegExp ( obj)
}
if ( obj instanceof Date ) {
return new Date ( obj)
}
const newObj = new obj. constructor ( )
for ( let key in obj) {
if ( obj. hasOwnProperty ( key) ) {
newObj[ key] = deepClone ( obj[ key] )
}
}
return newObj
}
let obj1 = deepClone ( obj)
obj1. a = 200
console. log ( obj, obj1)
function deepCloneWeakMap ( target, hash = new WeakMap ( ) ) {
const isObject = ( obj ) => typeof obj === 'obj' && obj !== null
if ( ! isObject ( target) ) return target
if ( hash. get ( target) ) return hash. get ( target)
const newObj = Array. isArray ( target) ? [ ] : { }
hash. set ( target, newObj)
for ( let key in target) {
if ( target. hasOwnProperty ( key) ) {
if ( isObject ( target[ key] ) ) {
newObj[ key] = deepCloneWeakMap ( target[ key] , hash)
} else {
newObj[ key] = target[ key]
}
}
}
return newObj
}
let obj1 = deepCloneWeakMap ( obj)
obj1. a = 200
console. log ( obj, obj1)
事件轮询
事件轮询机制 Event loop
JS的一大特点是单线程,所有任务都得排队,前一个任务结束,后一个任务才会执行,如果前一个任务执行时间过长,后一个任务就不得不等着
这里的任务分为两种: 宏任务 和 微任务
当宏任务执行完成后,会判断微任务队列中是否有任务,如果有,则把微任务放到主线程中并执行,如果没有,执行下一个宏任务
宏任务:在主线程上排队执行的任务,前一个任务执行完毕,才能执行下一个任务
分类:script全部代码(注意同步代码也属于宏任务)、setTimeout、setInterval、setImmediate、requestAnimationFrame (task 任务源)
1.宏任务所处的队列就是宏任务队列
2.第一个宏任务队列中只有一个任务:执行主线程的js代码
3.宏任务队列可以有多个
4.当宏任务队列的中的任务压部执行完以后会查看是否有微任务队列如果有先执行微任务队列中的所有任务,
最后再执行宏任务队列中的函数
微任务:不进入主线程,进入微任务队列的任务
分类:new Promise( ).then(回调) process.nextTick、MutationObserver
1.微任务所处的队列就是微任务队列
2.只有一个微任务队列
3.在上一个宏任务队列执行完毕后如果有微任务队列就会执行微任务队列中的所有任务
事件轮询机制的执行过程
1、代码执行过程中,宏任务和微任务分别放在不同的队列中
2、当某个宏任务执行完成后,会查看微任务队列是否任务,如果有,执行微任务队列中的所有微任务
3、微任务执行完成后,读取宏任务队列中排在第一个的宏任务(注意宏任务是一个一个读取),执行该宏任务,执行过程中遇到微任务,依次加入到微任务队列
4、宏任务执行完成,再次读取微任务队列中的微任务,并执行,以此类推
举个简单的例子,假设一个script标签的代码如下:
Promise. resolve ( ) . then ( function promise1 ( ) {
console. log ( 'promise1' ) ;
} )
setTimeout ( function setTimeout1 ( ) {
console. log ( 'setTimeout1' )
Promise. resolve ( ) . then ( function promise2 ( ) {
console. log ( 'promise2' ) ;
} )
} , 0 )
setTimeout ( function setTimeout2 ( ) {
console. log ( 'setTimeout2' )
} , 0 )
script里的代码被列为一个task,放入task队列。
循环1:
【task队列:script ;microtask队列:】
从task队列中取出script任务,推入栈中执行。
promise1列为microtask,setTimeout1列为task,setTimeout2列为task。
【task队列:setTimeout1 setTimeout2;microtask队列:promise1】
script任务执行完毕,执行microtask checkpoint,取出microtask队列的promise1执行。
循环2:
【task队列:setTimeout1 setTimeout2;microtask队列:】
从task队列中取出setTimeout1,推入栈中执行,将promise2列为microtask。
【task队列:setTimeout2;microtask队列:promise2】
执行microtask checkpoint,取出microtask队列的promise2执行。
循环3:
【task队列:setTimeout2;microtask队列:】
从task队列中取出setTimeout2,推入栈中执行。
setTimeout2任务执行完毕,执行microtask checkpoint。
【task队列:;microtask队列:】
event loop中的Update the rendering(更新渲染)
渲染的基本流程:
1、处理 HTML 标记并构建 DOM 树。
2、处理 CSS 标记并构建 CSSOM 树, 将 DOM 与 CSSOM 合并成一个渲染树。
3、根据渲染树来布局,以计算每个节点的几何信息。
4、将各个节点绘制到屏幕上。
可以看到渲染树的一个重要组成部分是CSSOM树,绘制会等待css样式全部加载完成才进行,所以css样式加载的快慢是首屏呈现快慢的关键点。
event loop和浏览器渲染时机
浏览器更新渲染会在event loop中的 宏任务 和 微任务 完成后进行,即宏任务 → 微任务 → 渲染更新(先宏任务 再微任务,然后再渲染更新
宏任务队列中,如果有大量任务等待执行时,将dom的变动作为微任务,能更快的将变化呈现给用户,这样就可以在这一次的事件轮询中更新dom
event loop 和 vue nextTick
vue nextTick为什么要优先使用微任务实现?
1、vue nextTick的源码实现,优先级判断,总结就是Promise > MutationObserver > setImmediate > setTimeout
2、优先使用Promise,因为根据event loop与浏览器更新渲染时机,使用微任务,本次event loop轮询就可以获取到更新的dom
3、如果使用宏任务,要到下一次event loop中,才能获取到更新的dom
node事件轮询
process.nextTick 是 Node.js 自身定义实现的一种机制,有自己的 nextTickQueue
process.nextTick执行顺序早于微任务
process.nextTick()
setTimeout()
setImmediate()
nodejs的事件轮询机制 :借助libnv库实现的
概括事件轮询机制,分为六个阶段
1. timers定时器阶段
计时和执行到点的定时器回调函数
2. pending callbacks
某些系统操作(例如TCP错误类型)的回调函数
3. idle, prepare
准备工作
4. poll轮询阶段(轮询队列)
如果轮询队列不为空,依次同步取出轮询队列中第一个回调函数执行,直到轮询队列为空或者达到系统最大的限制
如果轮询队列为空
如果之前设置过setImmediate函数
直接进入下一个check阶段
如果之前没有设置过setImmediate函数
在当前poll阶段等待直到轮询队列添加回调函数,就去第一个情况执行
如果定时器到点了,也会去下一个阶段
5. check查阶段
执行setImmediate设置的回调函数
6. close callbacks
关闭阶段执行close事件回调函数
process.nextTick能在任意阶段优先执行
相关题目
console. log ( '-------start------' )
setTimeout ( ( ) => {
console. log ( 'setTimeout()' )
} , 0 )
new Promise ( ( resolve, reject ) => {
for ( var i = 0 ; i < 5 ; i++ ) {
console. log ( i)
}
resolve ( )
} ) . then ( ( ) => {
console. log ( 'Promise()' )
} )
console. log ( '----end----' )
Promise. resolve ( )
. then ( function ( ) {
console. log ( 'promise0' )
} )
. then ( function ( ) {
console. log ( 'promise5' )
} )
setTimeout ( ( ) => {
console. log ( 'timer1' )
Promise. resolve ( ) . then ( function ( ) {
console. log ( 'promise2' )
} )
Promise. resolve ( ) . then ( function ( ) {
console. log ( 'promise4' )
} )
} , 0 )
setTimeout ( ( ) => {
console. log ( 'timer2' )
Promise. resolve ( ) . then ( function ( ) {
console. log ( 'promise3' )
} )
} , 0 )
Promise. resolve ( ) . then ( function ( ) {
console. log ( 'promise1' )
} )
console. log ( 'start' )
console. log ( 'script start' )
async function async1 ( ) {
await async2 ( )
console. log ( 'async1 end' )
}
async function async2 ( ) {
console. log ( 'async2 end' )
}
async1 ( )
setTimeout ( function ( ) {
console. log ( 'setTimeout' )
} , 0 )
new Promise ( ( resolve ) => {
console. log ( 'Promise' )
resolve ( )
} )
. then ( function ( ) {
console. log ( 'promise1' )
} )
. then ( function ( ) {
console. log ( 'promise2' )
} )
console. log ( 'script end' )
数组操作
for…in和for…of的区别
for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
手写Reduce
Array . prototype. Reduce = function ( fn, initValue ) {
if ( typeof fn !== 'function' ) {
throw new TypeError ( ` ${ fn} is not a function ` )
}
let pre, index
const arr = this . slice ( )
if ( initValue === undefined ) {
for ( let i = 0 ; i < arr. length; i++ ) {
if ( ! arr. hasOwnProperty ( i) ) continue
pre = arr[ i]
index = i + 1
break
}
} else {
pre = initValue
index = 0
}
for ( let i = index; i < arr. length; i++ ) {
if ( arr. hasOwnProperty ( i) ) {
pre = fn . call ( undefined , pre, arr[ i] , i, this )
}
}
return pre
}
console. log ( [ , , , 1 , 2 , 3 , 4 ] . Reduce ( ( pre, cur ) => pre + cur) )
手写Map
Array . prototype. Map = function ( fn, context ) {
if ( typeof fn !== 'function' ) {
throw new TypeError ( ` ${ fn} is not a function ` )
}
const arr = this . slice ( )
const list = new Array ( arr. length)
for ( let i = 0 ; i < arr. length; i++ ) {
if ( arr. hasOwnProperty ( i) ) {
list[ i] = fn . call ( context, arr[ i] , i, this )
}
}
return list
}
console. log ( [ 1 , 2 , 3 ] . Map ( ( item ) => item * 2 ) )
手写filter
Array . prototype. Filter = function ( fn, context ) {
if ( typeof fn !== 'function' ) {
throw new TypeError ( ` ${ fn} is not a function ` )
}
const list = [ ]
for ( let i = 0 ; i < this . length; i++ ) {
if ( fn . call ( context, this [ i] , i, this ) ) {
list. push ( this [ i] )
}
}
return list
}
console. log ( [ 1 , 2 , 3 , 4 ] . Filter ( ( item ) => item > 2 ) )
手写some
Array . prototype. Some = function ( fn ) {
if ( typeof fn !== 'function' ) {
throw new TypeError ( ` ${ fn} is not a function ` )
}
let result = false
for ( let i = 0 ; i < this . length; i++ ) {
if ( fn ( this [ i] , i) ) {
result = true
break
}
}
return result
}
console. log ( [ 1 , 2 , 3 , 4 ] . Some ( ( item ) => item > 6 ) )
console. log ( [ 1 , 2 , 3 , 4 ] . Some ( ( item ) => item > 2 ) )
手写Every
Array . prototype. Every = function ( fn ) {
if ( typeof fn !== 'function' ) {
throw new TypeError ( ` ${ fn} is not a function ` )
}
let result = false
let index = 0
for ( let i = 0 ; i < this . length; i++ ) {
if ( fn ( this [ i] , i) ) {
index++
if ( index === this . length - 1 ) {
reslut = true
}
}
}
return result
}
console. log ( [ 1 , 2 , 3 , 4 ] . Every ( ( item ) => item > 4 ) )
console. log ( [ 1 , 2 , 3 , 4 ] . Every ( ( item ) => item > 0 ) )
手写Flat
Array . prototype. Flat = function ( deep ) {
const arr = this . slice ( )
if ( deep === 0 ) return arr
arr. reduce ( ( pre, cur ) => {
if ( Array. isArray ( cur) ) {
return [ . . pre, ... cur. Flat ( deep - 1 ) ]
} else {
return [ ... pre, cur]
}
} , [ ] )
}
const arr1 = [ 0 , 1 , [ 2 , [ 3 , [ 4 , 5 ] ] ] ]
console. log ( arr1. flat ( ) )
console. log ( arr1. flat ( 2 ) )
console. log ( arr1. flat ( Infinity ) )
类型判断
基础数据类型
Undefined Null Number String Boolean Object Symbol BigInt (后面两个ES6新增)
基础数据类型(存放在栈中):Undefined Null Number String Boolean Symbol BigInt
引用数据类型(存放在堆中):Object (对象、数组、函数)
isNaN 和 Number.isNaN
NaN 是一个特殊的警戒值,它表示非数字,并且它不等于自身
NaN !== NaN (true)
typeof NaN === 'number' (true)
isNaN 会将传入的值进行数字转换,任何不能进行数字转换的值都返回true
Number.isNaN 会判断传入的值是否是数字,判断为数字再判断是不是NaN,不进行数据转换
转换到字符串
undefined -> 'undefined' null -> 'null' true -> 'true' false -> 'false'
数字正常转换(极大极小值使用指数形式)
Symbol直接转换(只允许显式强制转换,隐式强制转换会报错)
引用类型转换会调用toString()方法(toSting可以自定义)返回内部 [[class]] 的值
转换到数字
undefined -> NaN null -> 0 true -> 1 false -> 0
'' -> 0 含有非数字的字符串 -> NaN
Symbol 不能转数字
转换到boolean
undfeined null +0 -0 '' NaN false 都为false 其余的逻辑上都为true
类型转换
var a = ?
if ( a == 1 && a == 2 && a == 3 ) {
console. log ( 1 ) ;
}
var a = {
i: 1 ,
toString ( ) {
if ( i = 1 ) {
return this . i++
} else if ( i = 2 ) {
return this . i++
} else {
return this . i
}
}
}
var i = 0
Object. defineProperty ( window, 'a' , {
get ( ) {
return ++ i
}
} )
var a = [ 1 , 2 , 3 ]
a. toString = a. shift
if ( a == 1 && a == 2 && a == 3 ) {
console. log ( '成立' )
}
手写Typeof
function Typeof ( context ) {
return Object . prototype. toString . call ( context) . slice ( 8 , - 1 ) . toLowerCase ( )
}
const foo = ( ) => { }
const str = '1'
const boo = false
const n = null
const u = undefined
const s = Symbol ( )
const b = BigInt ( 9007199254740991 )
console. log ( Typeof ( foo) )
console. log ( Typeof ( str) )
console. log ( Typeof ( boo) )
console. log ( Typeof ( n) )
console. log ( Typeof ( u) )
console. log ( Typeof ( s) )
console. log ( Typeof ( b) )
手写instanceof
function Instanceof ( context, fn ) {
const proto = context. __proto__
if ( proto) {
if ( proto === fn. prototype) {
return true
} else {
return Instanceof ( proto, fn)
}
} else {
return false
}
}
const foo = ( ) => { }
const o = new Object ( )
const a = new Array ( )
const m = new Map ( )
const w = new WeakMap ( )
const s = new Set ( )
console. log ( Instanceof ( foo, Function) )
console. log ( Instanceof ( o, Object) )
console. log ( Instanceof ( a, Array) )
console. log ( Instanceof ( m, Map) )
console. log ( Instanceof ( w, WeakMap) )
console. log ( Instanceof ( s, Set) )
|| && 的返回值
|| -> true: 返回第一个操作数的值(不是条件结果) false: 返回第二个操作数的值(不是条件结果)
&& -> true: 返回第二个操作数的值(不是条件结果) false: 返回第一个操作数的值(不是条件结果)
map 和 Object 的区别
意外的键:
map不存在任何额外的键,只包含显示插入
object存在原型对象,可能会跟原型上的键名重复
键的类型
map的键值可以是任何类型
object的键值只能是string 或者 Symbol
键的顺序
map的键是有序的
object的键是无序的
大小
map 可以通过size轻松获取
object 只能手动计算
迭代
map可以直接迭代
object 需要获取键后才能迭代
性能
频繁删减下map优于object
map 和 WeakMap
Map 数据结构。它类似于对象,也是键值对的集合,
但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。
但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。
而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制
函数操作
compose
在函数式编程当中有一个很重要的概念就是函数组合
将一系列函数,通过compose函数组合起来,像管道一样连接起来,比如函数结合[f, g, h ],通过compose最终达到这样的效果: f(g(h()))
compose函数要求:可执行同步方法,也可执行异步方法,两者都可以兼容
function compose ( list ) {
const init = list. shift ( )
return function ( ... args) {
return list. reduce ( ( pre, cur ) => {
pre. then ( res => {
return cur . call ( undefined , res)
} )
} , Promise. resolve ( init . apply ( undefined , args) ) )
}
}
let sync1 = ( data ) => {
console. log ( 'sync1' )
return data
}
let sync2 = ( data ) => {
console. log ( 'sync2' )
return data + 1
}
let sync3 = ( data ) => {
console. log ( 'sync3' )
return data + 2
}
let syncFn = compose ( [ sync1, sync2, sync3] )
syncFn ( 0 ) . then ( ( res ) => {
console. log ( res)
} )
let async1 = ( data ) => {
return new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
console. log ( 'async1' )
resolve ( data)
} , 1000 )
} )
}
let async2 = ( data ) => {
return new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
console. log ( 'async2' )
resolve ( data + 1 )
} , 1000 )
} )
}
let async3 = ( data ) => {
return new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
console. log ( 'async3' )
resolve ( data + 2 )
} , 1000 )
} )
}
let composeFn = compose ( [ async1, async2, async3] )
composeFn ( 0 ) . then ( ( res ) => {
console. log ( res)
} )
函数柯里化
函数柯里化: 将使用多个参数的一个函数,转换成一系列使用一个参数的函数
函数柯里化的原理: 用闭包把参数保存起来,当参数的长度等于原函数时,就开始执行原函数
function curry ( fn ) {
if ( fn. length < 1 ) return fn ( )
const generator = ( ... args) => {
if ( fn. length === args. length) {
return fn ( ... args)
} else {
return ( ... args1) => {
return generator ( ... args, ... args1)
}
}
}
return generator
}
function fn ( a, b, c, d ) {
return a + b + c + d
}
let fn1 = curry ( fn)
console. log ( fn1 ( 1 ) ( 2 ) ( 3 ) ( 4 ) )
BOM
定时器
setTimeout固定时长后执行
setInterval间隔固定时间重复执行
setTimeout、setInterval最短时长为4ms
定时器不准的原因
setTimeout/setInterval执行的时间并不是确定的,由于 setTimeout/setInterval 是宏任务,
根据事件轮询,如果上一个宏任务阻塞延迟了,代码执行时间超过了定时器的时间就会出现定时器不准的情况
动画卡顿
不同屏幕的刷新频率不同,定时器只能设置固定的时间间隔,这个时间间隔可能跟屏幕的刷新间隔不同
requestAnimationFrame
requestAnimationFrame 是浏览器专门为动画提供的API
requestAnimationFrame刷新频率与显示器的刷新频率保持一致,使用该api可以避免使用setTimeout/setInterval造成动画卡顿的情况
requestAnimationFrame:告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵dom,更新动画的函数)
setTimeout、setInterval、requestAnimationFrame 三者的区别
1)引擎层面
setTimeout属于 JS引擎 ,存在事件轮询
requestAnimationFrame 属于 GUI引擎
JS引擎与GUI引擎是互斥的,也就是说 GUI引擎在渲染时会阻塞JS引擎的计算
这样设计的原因,如果在GUI渲染的时候,JS同时又改变了dom,那么就会造成页面渲染不同步
2)性能层面
当页面被隐藏或最小化时,定时器 setTimeout仍会在后台执行动画任务
当页面处于未激活的状态下,该页面的屏幕刷新任务会被系统暂停,requestAnimationFrame也会停止
setTimeout模拟setInterval
function mySetInterval ( fn, wait ) {
let timer
function interval ( ) {
fn ( )
timer = setTimeout ( interval, wait)
}
interval ( )
return {
cancel ( ) {
clearTimeout ( timer)
}
}
}
mySetInterval ( ( ) => {
console. log ( 11 )
} , 1000 )
setInterval模拟setTimeout
function mySetTimeout ( fn, wait ) {
let timer = setInterval ( ( ) => {
fn ( )
clearInterval ( timer)
} , wait)
}
mySetTimeout ( ( ) => {
console. log ( 22 )
} , 1000 )
web worker
Web Worker
专门处理复杂计算的,从此让前端拥有后端的计算能力
页面大量计算,造成假死
浏览器有GUI渲染线程与JS引擎线程,这两个线程是互斥的关系
当js有大量计算时,会造成UI 阻塞,出现界面卡顿、掉帧等情况,严重时会出现页面卡死的情况,俗称假死
计算时长超过多久适合用Web Worker
原则:
运算时间超过50ms会造成页面卡顿,属于Long task,这种情况就可以考虑使用Web Worker
但还要先考虑通信时长的问题,假如一个运算执行时长为100ms, 但是通信时长为300ms, 用了Web Worker可能会更慢
最终标准:
计算的运算时长 - 通信时长 > 50ms,推荐使用Web Worker
Web Worker的限制
1、在 Worker 线程的运行环境中没有 window 全局对象,也无法访问 DOM 对象
2、Worker中只能获取到部分浏览器提供的 API,如定时器、navigator、location、XMLHttpRequest等
3、由于可以获取XMLHttpRequest 对象,可以在 Worker 线程中执行ajax请求
4、每个线程运行在完全独立的环境中,需要通过postMessage、 message事件机制来实现的线程之间的通信