es6-es11系列3 对象

面向对象和面向过程

面向过程,考虑的是需求要拆解多少个步骤来实现;
面向对象,考虑的是这个需求里面有多少个对象,他们有哪些属性是必须的,行为之间的关系是如何的;
javascript 准确的说是基于对象(object- based)的语言

类与对象

类是对象的模版,定义了同一组对象共有的属性和方法,类可以有父类和子类。而对象就是对类进行实例化之后的结果,换句话说对象就是我们new出来的的实例。
构造函数内如果定义方法的话,最大的问题就是每次都会执行new Function的操作
例如:this.showName = function(alb){return a+b} 就等于 this. showName = new Function(a, b, ‘return a+b’)
正确的做法是应该把方法定义在原型(prototype)上。

静态属性和静态方法使用场景

比如定一个instanceCount的静态变量
再定一个getInstanceCount的静态方法
在constructor 中行进this.instanceCount ++
这样每次类被实例化的时候instanceCount的值就会+1,通过getInstanceCount则可以获得当前类已经被实例化了多少次

ES5 继承的几种方式

只继承父类的属性(构造函数继承),在子类的构造函数中调用父类的call方法把this传过去,例如

function Dog(name, color){
	Animal.call(this, name)
}

如果要继承父类的方法需要把子类的原型指向父类实例,并且把原型的构造函数指回来,这种方式叫原型继承,例如:

Dog.prototype = new Animal()
console.log(Dog.prototype.constructor) // function Animal() {this.name = 'animal'}
Dog.prototype.constructor = Dog // function Dog(name, color) {Animal.call(this, name)}

如果将上面的两种方法组合起来使用,则可以同时继承属性和方法,所以叫组合继承

ES6的继承

es6 使用extends继承class
在子类的constructor中使用super(name)的方式继承父类的属性,类似es5的构造函数继承
es6中定义顶层的属性的方式使用get语法 ,例如

get sex(){
	return ‘male’
}
let p1 = new Person(‘xx’)后

通过p1.sex获得
这种顶层属性不能被外部直接修改,比如p1.sex=111会报错
如果要改变sex的值,则需要使用set语法
优点是可以在属性设置和获取的时候做前置的拦截操作,例如:

constructor(){
	this._sex = -1
}
get sex(){
	if(this._sex ===1) return ‘male’
 	if(this._sex === 0) return ‘female’
}
// sex的值仅接受0和1
set sex(val){
	if(val ===0 || val ===1) this._sex = val
}

class只能用static定义的静态方法,不能定义属性,父类的静态方法可以被子类调用,但是不可以在实例下调用。
如何非要在类上面定义静态属性,可以直接写Person.count = 5 但是不推荐

Symbol

Symbol不是对象,不能被new,也不能添加属性和方法。

let s1 = Symbol(‘foo’) // 参数可以添加描述,可以调用s1.description来输出描述
let s2 = Symbol(obj) // 如果参数是对象则会自动调用对象下的toString(),可以可以手动在对象下面自定义一个toString方法让它自动被调用

如果使用Symbol.for定义的Symbol则可以相等。
let s1 = Symbol.for(‘foo’) 在全局定义个key为foo的Symbol,可以使用Symbol.keyfor(s1) 打印Symbol全局的key值

使用场景一,解决对象的key重名后覆盖问题

const stu1 = Symbol(‘tom’)
const stu2 = Symbol(‘tom’)
const data = {
	[stu1]: {address:’xxx’},
	[stu2]: {address:’xxx’},
}

场景二,使用Symbol来模拟私有属性

const s1 = Symbol(‘a’)
constructor(){
	this[s1] = 111
}
for in ,Obejct.keys(),Object.getOwnPropertySymbols都不能遍历Symbol属性
只有 用for(let key of Reflect.ownKeys(user))  可以

场景三,魔术字符串
const shapeType = {
	triangle: Symbol(), // 无需给定具体的字符串,比如:’triangle’
	circle: Symbol()
}
switch (shape){
	case shapeType.triangle:
	case shapeType.circle:
…
}

SET

基本语法

let s = new Set([1,1,2]) // [1,2]
s.add(5).delete(2).clear().size
s.has(1) // false

set 可以被forEach和for of遍历
求数组的交集

let s1 = new Set(arr1)
let s2 = new Set(arr2)
let result = new Set(s1.filtter(item => s2.has(item)))

weakSet // 只能添加对象

let ws = new weakSet()
ws.add(1) // 报错

weakSet不能使用forEach
弱引用,不会被垃圾回收机计数,当引用的对象消失后weakSet也会消失

map

map的key 跟对象不同,可以传对象和数组
常用的方法
set
get
has
size
delete
创建map的基本语法
new Map([‘name’,’tom’],[‘age’,12])
使用forEach for of 进行遍历

map优势
size has 方法更便捷性能更好 key不会跟原型的属性冲突
weakMap 的key只支持引用类型 不能遍历也没有size方法

应用场景

let el = document.get elements by tag name()
let els = new map()
els.set(el,’desc’)

用map存dom元素用key来遍历管理很方便
由于弱引用的机制 还能防治内存泄露

字符串

es5的unicode 取值范围是0000-ffff
es6 使用/u{}的方式增加了取值范围
\hhh 8进制字符
\x12 16进制字符
for of 遍历字符串

模版字符串可以直接换行 无需写+和\n
模版字符还支持函数方式,例如foo’aaa${123}bbb’

es6新增的方法
includes
startsWith
endsWith
repeat(7)

正则表达式

es5中正则中的修饰符
i 忽略大小写
m 多行
g 全局

es6新增两个修饰符
y 粘连修饰符
匹配失败后会返回到起始位置

u unicode 修饰符

数值

b0和 0o来表示十进制和8进制
5/0 会得到无穷大 infinity
‘a’ / 5 会得到 NaN not a number
Number.isFinite() 传入 字符串或布尔时也会返回false
Number.isNaN 注意此方法通常判断值是否是一个数字
es6中会把一些全局的方法放到对应的模块中,例如parseInt parseFloat
Number.isInteger() 是否是整数
Number.trunc 取整
Math.sign 判断是正数还是0还是负数
Math.cbrt 立方根

代理 Proxy

es5中对对象进行拦截使用 Object.defineProperty()
使用Proxy拦截对象,实现私有属性(下划线开头)禁止访问

let user = {
	name:’xxx’,
	age: 18,
	_gender: ‘male’	
}
user = new Proxy(user,{
	get(target, key) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key]
		}
	},
	set(target, key, val) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key] = val
		}
	}
	 has (target, key) {
                    if(key[0] === "_") { // key.startsWith(‘_’)
                        return false;
                    }
                    return key in target;
         }
	deleteProperty(key){
		if(key.startsWith(‘_’){
			throw new Error(‘不可删除’)
		} else {
			delete target[key]
			return ture	
		}
	}
	ownKeys(target){
		return Object.keys(target).filter(key => !key.startsWith(‘_’))
	}
})
使用Proxy拦截函数调用
let sum = (…args) =>{
	let num = 0
	args.forEach(item => {
		num += item
	})
	return num
}
sum = new Proxy(sum, {
	// 当前函数,上下文this,参数
	apply(target, ctx, agrs) {
		return target(…args) * 2
	}
})
// 使用Proxy拦截类的构造函数
let User = class {
	constructor (name){
		this.name = name
	}
}
User = new Proxy(User, {
	// 当前类,参数,目标类
	constructor(target, args, newTarget){
		return new target(…args)
	}
})

反射 Reflect

Reflect 设计的目的
1. 将Object属于语言内部的方法放到Reflect身上
2. 修改某些Object方法的返回结果
3. 让Object操作变成函数行为
4. Reflect对象和Proxy的对象方法一一对应

es6-es11系列1-2基本语法

EECMA 组织每年都会更新一个版本
Es6对应 es2015
Es11 对应 es2020
以此类推

比较典型的es6新特性

可选链

list  =  res?.data?.list

使用SymboSymbo 消除魔法字符

const ls ={
        running:Symbol(),
        finish:Symbol ()
}
function  a (state){
        switch (state)
        case ls.running:
          console.log  r
        case ls.finish:
        ...
a(ls)

类数组转数组

Array.from(noteList)

利用nrm命令 快速切换源地址

npm i -g nrm  
nrm ls
nrm test taobao 
nrm  use  taobao

基本语法

作用域

delete  只能删除属性  不能删除变量
let定义的变量不会像var那样被挂到window对象上
let  不会像var那样重复声明
let  不存在变量提升 ,变量定义之前不能被打印出来
let  会形成暂时性死区,变量未声明之前不能被使用
let有块级作用域

es5 中如何定义常量

使用Object.definepropert方法
Const定义的引用类型  可以被添加新的属性
可以通过object.freeze  冻结对象,仅能对对象做浅冻结
结构赋值可以设置默认值,例如  let [  a,  b,  c  =2 ]  =  arr

es5 的数组遍历方法

forEach 方法不支持  break和  continue  无法终止循环
map 可以返回数组中的每一项组成一个新 的数组  例如  item +  1
filter  返回满足条件的数组项  例如  return item >2
some  判断数组中的值只要有一个满足条件则返回ture
every  要求没一项都满足条件才返回ture
reduce 第一个参数是上次的返回值,第二个参数是当前的值,适合做累加,求最大值,去重
for in 遍历数组时会把隐式原型的值也遍历出来

es6 数组新增特性

find  可以返回数组中满足条件的数组项
findIndex  返回满足条件的数组项下标
for  of  可以遍历数组的index  和  value
for(  [index,value]  of arr.entries() )
Array.of(1,2,3) //通过参数创建数组 [1,2,3]
arr.copyWithin(1,3) 下标为1的值替换下标为3开始到末尾的所有值
[1,2,3].fill(‘a’,0,2) // [‘a’, ‘a’, 3] 将数组中下标0到2的值填充为 ‘a’
arr.includes(1,2) 数组中是否包含1和2
indexOf 不能检测到 NaN includes则可以

类数组相关

html collectionname: get elements  by tag name
nodelist :   query selection
判断是否是数组
instanceOf array
调用对象的push方法  如果报错则不是数组
arguments 是对象不是数组
es5遍历类数组使用Array. prototype. forEach. call(arguments)
Es6 新增Array.from方法来转换对象为数组

函数的参数
es6的参数可以设置默认值
有默认值的参数建议放到最后,意味着可以省略
函数的length 返回的是为指定默认值的参数的数量
参数作用域的特性,如果参数设置了默认值则会形成作用域

let x=1
fun foo(x, y=x){
    console.log(y) // 2
}
foo(2) 

函数有name属性 如果new Function 则name值为anonymous ,如果使用了foo.bind({}) 则name值为bond

扩展运算符和rest参数

扩展运算符
打散…arr
合并 […arr1,…arr2] 或arr1.push(…arr2)
剩余参数

fn foo(x,...y) // 1 [2,3]
foo(1,2,3)

let [x,...y] =[1,2,3]

箭头函数特性
没有this
不能被new
没有arguments对象,可以使用…args代替

对象的扩展

Object.is(NaN,NaN) // true
console.log(NaN == NaN) // false
Object.is(Obj1,Obj2) // false
console.log(Obj1 == Obj2) // false 

因为引用类型的对象指向的内存地址不一样

Object.assign(a, b)
console.log(‘name’ in Person) // ture
let arr = [1,2,3]
2 in arr // 数组对象的in 判断的是下标处是否有值

对象的几种遍历方式

for(let key in obj){
	let value = obj[key}
}
Object.keys(Obj).forEach(key =>{
	let value = obj[key}
})
Object.getOwnPropertyNames(obj).forEach(key =>{
	let value = obj[key}
})
Reflect.ownKeys(obj).forEach(key =>{
	let value = obj[key}
})

深拷贝和浅拷贝
浅拷贝

JSON.stringify(obj)
JSON.parse(str)

弊端:
Date 对象变为字符串
RegExp、Error 对象变为空对象 {}
函数、undefined、Symbol 属性丢失
NaN、Infinity、-Infinity 变为 null
enumerable 为 false 的属性丢失
循环引用的对象不能正确拷贝

深拷贝网络上自行搜索吧,很常用

判断是对象还是数组 除了instanceOf 也可以用

Object.prototype.toString.call(data).slice(8,-1)

2021年前端基础面试题 18 额外一些面试真题

typeOf 返回的数据类型有哪些
手写深度比较函数,模拟lodash的 isEqual()
string.split()和arr.jion()的区别
数组中哪些方法是纯函数,哪些是非纯函数
数组中的splice和slice的区别,splice会改变原数组的值

网红题目 [1,2,3].map(parseInt) // [1,NaN,NaN]

原因: parseInt(17,2) // NaN, 因为parseInt的第个参数进制基数radix必须是10、8、16、2 parseInt(10,2) // 2
parseInt(10,8) // 8
parseInt(10,16) //16

let a = function(){} 和 function a(){} 的区别

let a是函数表达式,function a是函数声明,函数声明可以在代码执行前预加载,所以在function a上面执行 a()不会报错;

new Object()和 Object.create()的区别

new Object() 等于 let a = {}, 它是有原型属性的 而Object.create()参数中可以指定原型对象,所以当传入null的时候,例如Object.create(null) 它是没有原型属性的。

异步操作的相关题目

let i 
for(i = 1; i<=3; i++){
    setTimeout(function(){
       console.log(i)
    })
} 
// 4 4 4 

解释如果不使用setTimeout结果是 1 2 3,用了之后则会先执行同步任务的for循环,结束之后i的值为4,因为循环了3次,webApi中会往事件队列中放入3个function,所以打印了3次i

作用域相关题目

let a = 100
function test(){
    alert(a)
    a = 10
    alert(a)
}
test()
alert(a)
// 100 10 10

捕获错误的2种方式 try catch和window.onerror

获取url参数的2种方式

老:location.search // '?a=1&b=2' 需要自己实现个方法去获取对应的参数 新:URLSearchParams
let params = new URLSearchParams(location.search)
console.log(params.get('a'))

将url参数解析为js对象

function queryToObj(){
    const res = {}
    cosnt pList new URLSearchParams(location.search)
    pList.forEach((val,key)=>{
        res[key] = val
    })
    return res
}

数组拍平
数组去重
手写深拷贝

RAF( window.requestAnimationFrame)

可以让动画更流畅,比setTimeout更适合做动画,自动控制频率,dom隐藏或窗口切换后会暂停,性能更好 比如我们要做60帧的动画,1秒要用setTimeout执行60次

2021年前端基础面试题 16-17 运行环境、性能和安全

16 运行环境、性能和安全

XSS/XSRF 攻击

预防XSS:替换特殊字符,如< 变为< 可以通过xss工具 npm i xss


17 性能优化

提升加载速度

减少资源体积(压缩代码)
减少访问次数(合并代码、SSR、缓存)
使用更快的网络(CDN)

提升渲染速度

CSS放在header内,js放在body最下面
尽早开始执行js,用DOMContentLoaded触发
懒加载
对dom查询进行缓存
频繁的dom操作合并到一起后统一插入
节流和防抖

页面加载过程

DNS(domain name server) 解析: 域名 – ip
HTTP
生成DOM Tree
生成CSSOM
整合DOM Tree和CSSOM合成Render Tree
遇到script暂停渲染,完成后继续
图片加载不会阻塞render tree

window.addEventListener('load') // 页面全部加载完成后触发,包括图片和视频
window.addEventListener('DOMContentLoaded') //Dom渲染完之后触发,无需等待图片和视频

图片懒加载的方式

将图片地址先写死一个预览的小图地址,而真实的图片地址可以放在例如data-relasrc属性中,当屏幕滚动到需要显示这个图片的位置时,替换src



2021年前端基础面试题 15 开发环境

git使用

假如在a分支上不小心修改了很多文件后发现其实应该在b分支上修改,这时直接切换分支已经不行了,注意新建的文件m不受影响。
可以先执行git stash把已修改的文件暂存起来。切换到b分支后再执行git stash pop把文件取出来

charles使用

手机抓包的过程

webpack

如何创建开发环境和生成环境2套配置
使用模板插件,配置不同的loader,生产环境的文件名[contenthash]

babel

基本的es6转es5配置,使用预设插件

Liunx

mkdir //创建文件夹
touch a.js //新建文件
open . // 打开当前目录
ls ll //显示当前目录下的文件列表
cat index.html //把文件内容打印到控制台
rm -rf abc (r= recurrence f= force) //递归强制删除目录
mv a.html b.txt //修改文件名, 或者移动文件到目标文件夹
cp a.js b.js //拷贝
vimtutor //系统自带的vim教程

2021年前端基础面试题 13-14 存储和http

3种存储

cookie 主要是设计用来做server和浏览器通讯,所以并不适合做本地存储
缺点:最大容量4k,每次http请求都会带上,使用document.cookie =”方式修改!api太简陋
localstorage 和 sessionstorage 每个域名最大5m,不会放入hhtp请求中,使用setItem(使用方便

http状态码

1xx 服务端收到请求
2xx 请求成功
3xx 重定向
4xx 客户端错误
5xx 服务端错误

常见的状态码

200 成功
301 永久重定向
302 临时重定向
304 资源未被修改
404 未找到
403 无权限
500 错误
504 超时

http methods

get 查
post 增
patch put 改
delete 删

restful api

相对于传统的api设计, restful不会把url设计成一个功能,而是一个资源。
例如restful不使用url参数
api/list?index=2
api/list/2

常见的header

request header

accept 接受的数据格式
accept-encoding 接受的压缩格式如gzip
accept-language
connection keep-alive
cookie
host
user-agent
content-type application/json

response header

content-type application-json
content-length
content-encoding 服务器返回的压缩格式
set-cookie 服务器通过这个来设置cookie

http缓存

通常静态资源都会被缓存,比如js,css,images 。
为了避免静态资源被缓存通常在webpack打包时会在静态资源文件名后面加上hash字符串,例如index.3hjdjyu73hd8.js

强制缓存

服务器会在response header之中返回catch-control 来控制缓存的逻辑,例如max-age=3500000(秒)
no-catch 客户端无需缓存
no-store 客户端和服务器都不缓存,不常见
expires 是老标准,已经被catch-control取代

协商缓存

服务端判断请求的资源是否需要缓存,例如发现请求资源没有变化,则返回304告诉客户端无需请求。
服务端通过下发资源标识给客户端,客户端下次访问的时候带上资源标识返回给服务端。服务端再根据资源标识来判断此资源是否过期。
客户端下发资源标识分为两种:
last-modified 资源最后修改时间(秒)
e-tag 唯一标识 优先级最高
对应客户端请求的资源标识
if-modified-since
if-none-match

2021年前端基础面试题 12 AJAX

手写简易的ajax

const xhr = new XMLHttpRequest();
// method:要是用的HTTP方法,url:请求的主体,async(可选):false为同步,true为异步,默认为同步
xhr.open('GET', '/api', false);
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            alert(xhr.responseText);
        };
    };
};
xhr.send(null); //如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null。

xhr.readyState

0 – (未初始化)还没有调用send()方法
1 – (载入) 已调用 send()方法,正在发送请求
2 – (载入完成) send()方法执行完成,已经接受到全部响应内容
3 – (交互)正在解析响应内容
4 – (完成)响应内容解析完成,可以在客户端调用

status 状态码

301 永久重定向
302 临时重定向
301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

304 缓存
404 未找到
403 无权限
5xx 服务端报错

若想设置POST请求版,则需更改

xhr.open里的参数为xhr.open(‘POST’, url, true/false);
xhr.send里的参数为xhr.send(p);,

浏览器同源策略 协议 域名 端口 三者缺一不可

jsonp实现跨越
window.callback=fn
callback({data:1})

2021年前端基础面试题 9-11 DOM BOM

DOM (document object model)
BOM (browser object model)

DOM的数据结构是树型结构

attribute 是修改DOM标签,会改变HTML结构
P.setAttribute(‘style’,’width:100px’)
property 是修改Js对象,性能更好,推荐用这种方式
P.style.width =100px
2者都会触发DOM渲染

插入子节点

divEl.appendChild(P)
注意如果P存在,会变成移动节点

获取父节点

P.parentNode

获取子元素

divEl.childNodes
注意childNodes中会包含nodeType为3的text类型节点,需要过滤出nodeType等于1的b元素标签类型

nodeList转数组

Array.prototype.slice.call(NodeList)

删除子元素

divEl.removeChild(P1)

DOM操作性能优化注意点

将循环插入或修改DOM的操作放入一个临时片段中,遍历完成后再统一插入到容器节点中,说白了就是减少不必要的渲染次数。

判断ua的方法

这个自行在网络上搜索吧,可能随着技术更新每年都不一样

location属性

hash 设置或返回从井号 (#) 开始的 URL(锚)。
host 设置或返回主机名和当前 URL 的端口号。
hostname 设置或返回当前 URL 的主机名。
href 设置或返回完整的 URL。
pathname 设置或返回当前 URL 的路径部分。
port 设置或返回当前 URL 的端口号。
protocol 设置或返回当前 URL 的协议。
search 设置或返回从问号 (?) 开始的 URL(查询部分)。

History 对象属性

length 返回浏览器历史列表中的 URL 数量。

History 对象方法

back() 加载 history 列表中的前一个 URL。
forward() 加载 history 列表中的下一个 URL。
go() 加载 history 列表中的某个具体页面。

如何阻止超链接跳转 e.preventDefault
如何为下拉加载的图片列表添加点击事件, 事件代理

2021年前端基础面试题 8 异步编程

1. event loop 画图说明

  • JS 是单线程运行
  • 异步是基于回调来实现
  • event loop 就是异步回调的实现原理

Call Stack 同步代码

代码执行时放入Call Stack 执行完成后移出

Web APIs 浏览器的接口

setTimeout()是web Api的定时器方法,不是es6的语言
执行setTimeout时,将它放入Web APIs里面等待5秒,后面到第5秒出发时会放入Callback Queue

接下来执行第三行代码打印Bye,同样也是执行时放入Call Stack 执行后移出Call Stack,这时整个代码同步的部分都已经执行完了。

Event Loop 事件轮询

5秒后 Web APIs里面的回调函数被触发,将cb方法放入了 CallBack Queque队列中

Event Loop 轮询CallBack Queue 发现有cb() 则放入Call Stack中执行并移出

cb()方法中有console.log() 则先执行console.log 然后执行完移出,最终再把cb()移出

Promise相关

promise的三种状态

resolved只能触发then,reject只能触发catch,例如下面的代码

Promise.resolve(100).then(data=>{
    console.log(data) // 100
})

catch 中如果正常返回,则后续状态为resolved,否则为rejected

面试题:then和catch的链式调用

Promise.resolve().then(()=>{
    console.log(1) // 1 
    throw new Error('err') // then里面有报错会触发后续的catch
}).catch(()=>{
    console.log(2) // 2 // catch中没有报错会触发后续的then
}).then(()=>{
    console.log(3) // 3
})

async/await

!(async  function (){
await  1
})()

async 返回Promise
await 等于promise的then
await 200 等于 await Promise.resolve(200)

宏任务和微任务

宏任务:settimeout ajax dom事件
微任务:promise await
宏任务在dom渲染后执行,微任务反之
微任务是es6语法 不会放到webapi区域,所以先执行

2021年前端基础面试题 1-7

1 CSS

line-height 的继承机制
父元素行高20px:子元素行高直接继承20px
父元素2或者1.5:子元素行高就是font-size的2倍或者1.5倍
父元素200%:子元素行高是200%乘以父元素的font-size而不是乘子元素的font-size

flex画筛子三

box  {
    display: flex;
    justify-content: sapce-between;
}

item:nth-child(2)  {
    align-self :  center ;
}

item:nth-child(3){
    aligin-self:flex-end;
}

响应式布局

rem vw vh vmax vmin 通过媒体查询的方式动态的设置跟字体的大小

rem的计算机制

(N)rem = html的font-size *( N)

例1:

默认的html的font-size是16px,div的font-size设置为1rem就是16*1=16px
为了便于理解,我们用F表示html的font-size,用R表示rem,用P表示px。
从上面的结果就可以得到公式 P = F * R
F = 16px
R = 1
P = 16 *1
结果P就是16px

例2:

如果html的font-size设置为100px
目标像数要求为16px
那div的font-size设置0.16rem
rem就是目标像数除以根元素
得到公式 R = P / F (rem = 目标px / html的font-size)
第三个公式
F = P / R

2 JS

变量类型,事件机制

值类型,引用类型,手写深拷贝,事件冒泡,事件代理

原型、原型链 、类 、继承

画出原型链的类图,解释supper,es5继承方式

// 构造函数 
function Parent1(name) {
  this.name = name;
  this.age = 12;
}
function Child1() {
  Parent1.call(this,'son');
  this.type = 'child1';
}
console.log( new Child1().name); //son

闭包

作用域,自由变量,手写bind

Function.prototype.myBind = function (){
    const args = Array.prototype.slice.call(arguments)
    const t = args.shift() // 第一项为传入的this
    const that = this
    return function (){
        retrun that.apply(t, args)    
    }
}

实际的应用场景
隐藏数据,比如jquery的自定义事件也是这样隐藏对象数据,只暴露方法

事件流,事件循环

事件流分为3个阶段:
(1)捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自下而上向Document节点传播的阶段。

webpack构建流程

初始化 – 配置
编译 – loader,plugin
输出 – chunk

数组求和

// Accumulator (acc) (累计器)
// Current Value (cur) (当前值)
// Current Index (idx) (当前索引)
// Source Array (src) (源数组)
var sum = [0, 1, 2, 3].reduce(function(accumulator, currentValue) {
  return accumulator + currentValue
}, 100) // 从100开始累加
console.log(sum) // 106

使用promise 封装一个图片加载的方法

function loadImg(src) {
    const p = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img')
            img.onload = () => {
                resolve(img)
            }
            img.onerror = () => {
                const err = new Error('img load err' + src)
                reject(err)
            }
            img.src = src
        }
    )
    return p
}

const url1 = 'xxx1'
const url2 = 'xxx2'
loadImg(usr1).then(img1 => {
    console.log(img1.width)
    return img1
}).then(img1 => {
    console.log(img1.height)
    return loadImg(url2)

}).then(img2 => {
    console.log(img2.width)
})

for of 和for in forEach的区别

for of 可以处理异步的遍历

const nums = [1, 2, 3]

// 先遍历完数组,先执行3次异步调用
nums.forEach(async (i) => {
    const res = await getList(i)
    console.log(res)
})

// 遍历移数组一次,执行一次异步调用,返回结果后再遍历数组下一项
!(async function () {
    for (let i of nums) {
        const res = await getList(i)
        console.log(res)
    }
})()