一、变量类型和变量计算 值类型和引用类型 常见值类型 1 2 3 4 5 let a const str = 'abc' const n = 100 const b = true const s = Symbol ('s' )
常见引用类型 1 2 3 4 const obj = {x :100 }const arr = ['a' , 'b' , 'c' ]const n = null function fn ( ) {}
值类型和引用类型的区别 值类型的数据存在栈中,引用类型值只存地址,数据存在堆中
typeof和深拷贝 typeof能判断哪些类型 识别所有值类型 undefined string number boolean symbol 1 2 3 4 5 let a typeof a const str = 'abc' typeof str const n = 100 typeof n const b = true typeof b const s = Symbol ('s' ) typeof s
能判断函数 1 function fn ( ) {} typeof arr
能识别引用类型(不能再细分) 1 2 3 const obj = {x :100 } typeof obj const arr = ['a' , 'b' , 'c' ] typeof arr const n = null typeof n
深拷贝
判断是值类型还是引用类型
判断是数组还是对象
递归
本地安装一个服务
1 2 npm install http-server -g http-server -p 8001
深拷贝代码
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 let obj1 = { name: 'bob' , age: 20 , address: { city: 'beijing' , home: 'changping' }, tech: ['c' , 'js' , 'html' , 'css' , 'vue' ] } let obj2 = deepClone(obj1)obj2.address.city = 'shanghai' console .log(obj1.address.city)function deepClone (obj ) { if ( typeof (obj) !== 'object' || obj == null ){ return obj } let result if (obj instanceof Array ){ result = [] }else { result = {} } 如果obj是对象,key为键,是数组,key为下标 for (let key in obj){ if (obj.hasOwnProperty(key)){ result[key] = deepClone(obj[key]) } } return result }
变量计算-类型转换 字符串拼接 1 2 3 let a = 100 + 10 let b = '100' + 10 let c = true + '10'
== 1 2 3 4 5 100 == '100' 0 == '' 0 == false false == '' null == undefined
何时使用 === 何时使用 == 只有一种情况使用 ==
1 2 3 4 <!-- 除了 == null 之外,其他一律使用 === --> const obj = {x :100 }if (obj.a == null ){}<!-- 相当于 if (obj.a === null || obj.a === undefined ) -->
if语句和逻辑运算 truly变量: !!a === true 的变量 falsely变量: !!a === false 的变量 以下为falsely变量
1 2 3 4 5 !!0 !!NaN !!'' !!undefined !!false
if语句实际上是判断truely变量和falsely变量 逻辑判断 1 2 3 console .log(0 && 10 ) console .log('' || 'abc' ) console .log(!window .abc)
二、原型和原型链 题目
如何判断一个变量是不是数组
手写一个jquery,考虑插件和扩展性
class的原型本质,怎么理解
知识点
class和继承
类型判断 instanceof
原型和原型链
class和继承 class
constructor
属性
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Student { constructor (name, number){ this .name = name this .number = number } sayHi(){ console .log( `我叫 ${this .name} , 我的学号是 ${this .number} ` ) } } const xialuo = new Student('夏洛' , 100 )console .log(xialuo.name, xialuo.number)xialuo.sayHi()
继承
extends
super
扩展或重写方法
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 class People { constructor (name){ this .name = name } eat(){ console .log(`${this .name} eat something` ) } } class Student extends People { constructor (name, number){ super (name) this .number = number } sayHi(){ console .log( `我叫 ${this .name} , 我的学号是 ${this .number} ` ) } } const xialuo = new Student('夏洛' , 100 )console .log(xialuo.name, xialuo.number)xialuo.sayHi() xialuo.eat() class Teacher extends People { constructor (name, major){ super (name) this .major = major } teach(){ console .log(`${this .name} 教授 ${this .major} ` ) } } let wanglaoshi = new Teacher('王老师' , '语文' )console .log(wanglaoshi.name, wanglaoshi.major)wanglaoshi.eat() wanglaoshi.teach()
原型 类型判断-instanceof 1 2 3 4 5 6 7 xialuo instanceof Student xialuo instanceof People xialuo instanceof Object [] instanceof Array [] instanceof Object {} instanceof Object
class 实际上是 function ,可见是语法糖
1 2 3 4 5 6 7 8 typeof (Student) typeof (People) console .log(xialuo.__proto__)console .log(Student.prototype)console .log(xialuo.__proto__ === Student.prototype)
原型关系
每个class都有显式原型prototype
每个实例都有隐式原型proto
实例的proto 指向class的prototype
基于原型的执行规则 获取xialuo.name或执行xialuo.sayHi()时
先在自身属性和方法寻找
找不到就自动去proto 寻找
原型链 1 2 3 4 5 6 console .log(Student.prototype.__proto__)console .log(People.prototype)console .log(Student.prototype.__proto__ === People.prototype)xialuo.hasOwnProperty('name' ) xialuo.hasOwnProperty('sayHi' )
通过显示原型向上找隐式原型,一层一层向上找,最后object的proto 是null
instanceof 通过隐式原型向上找,能不能找到显示原型,能找到,就返回true
手写一个jquery,考虑插件和扩展性 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 class jQuery { constructor (selector) { const result = document .querySelectorAll(selector) const length = result.length for (let i = 0 ; i < length; i++) { this [i] = result[i] } this .length = length this .selector = selector } get (index) { return this [index] } each(fn) { for (let i = 0 ; i < this .length; i++) { const elem = this [i] fn(elem) } } on(type, fn) { return this .each(elem => { elem.addEventListener(type, fn, false ) }) } } jQuery.prototype.dialog = function (info ) { alert(info) } class myJQuery extends jQuery { constructor (selector) { super (selector) } addClass(className) { } style(data) { } }
三、作用域和闭包 题目
this在不同的应用场景下,如何取值
手写bind函数
实际开发中闭包的应用场景
知识点
作用域和自由变量
闭包
this
作用域
全局作用域
函数作用域
块级作用域
自由变量
一个变量在当前作用域没有定义,但被使用了
向上级作用域,一层一层寻找,直到找到为止
如果到全局作用域都没找到,报错 xx is not defined
闭包 作用域应用的特殊情况
函数作为参数被传递
函数作为返回值被返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function create ( ) { let a = 100 return function ( ) { console .log(a) } } const fn = create()const a = 200 fn() function print (fn ) { const a = 200 fn() } const a = 100 function fn ( ) { console .log(a) } print(fn)
所有自由变量的查找,是在函数定义的地方向上查找,不是在执行的地方向上去找
this
作为普通函数
使用call apply bind
作为对象方法被调用
在class方法中调用
箭头函数
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 33 34 35 36 37 38 39 40 41 42 43 44 45 function fn1 ( ) { console .log(this ) } fn1() fn1.call({x :100 }) const fn2 = fn1.bind({x :200 }) fn2() const zhangsan = { name: '张三' , sayHi(){ console .log(this ) }, wait(){ setTimeout(function ( ) { console .log(this ) }) } } const zhangsan = { name: '张三' , sayHi(){ console .log(this ) }, wait(){ setTimeout(() => { console .log(this ) }) } } class People { constructor (name){ this .name = name } } const zhangsan = new People('张三' )
手写bind函数 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 function fn1 (a,b,c ) { console .log('this' ,this ) console .log(a,b) return 'this is fn1' } const fn2 = fn1.bind({x :100 },10 ,20 ,30 )const res = fn2()console .log(res)Function .prototype.bind1 = function ( ) { const args = Array .prototype.slice.call(arguments ) const t = args.shift() const self = this return function ( ) { return self.apply(t, args) } }
闭包的应用
隐藏数据
做一个简单的cache工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCache ( ) { const data = {} return { set : function(key, val){ data[key] = val }, get : function(key){ return data[key] } } } const c = createCache()c.set('a' ,100 ) console .log(c.get('a' ))
从0到9,点击每个元素时,弹出对用的数字
1 2 3 4 5 6 7 8 9 10 for (let i = 0 ;i < 10 ; i++){ let a = document .createElement('a' ) a.innerHTML = i + '<br>' a.addEventListener('click' , function (e ) { e.preventDefault() alert(i) }) document .body.appendChild(a) }
四、同步和异步 题目
同步和异步的区别是什么?
手写用promise加载一张图片
前端使用异步的场景有哪些?
1 2 3 4 5 6 7 8 9 10 11 console .log(1 )setTimeout(function ( ) { console .log(2 ) },1000 ) console .log(3 )setTimeout(function ( ) { console .log(4 ) },0 ) console .log(5 )
知识点 单线程和异步 js是单线程语言,只能同时做一件事 浏览器和nodejs已支持js启动进程,如web worker js和dom渲染公用同一个线程,因为js可修改dom结构 遇到等待(网络请求,定时任务)不能卡住,所以需要异步 基于callback函数形式
1 2 3 4 5 6 7 8 9 10 11 console .log(100 )setTimeout(function ( ) { console .log(200 ) }) console .log(300 )console .log(100 )alert(200 ) console .log(300 )
异步不会阻塞代码执行,同步会阻塞代码执行
应用场景 网络请求,如ajax图片加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 console .log('start' )$.get ('./data.json', function(data){ console .log(data) }) console .log('end' )console .log('start' )let img = document .createElement('img' )img.onload = function ( ) { console .log('loaded' ) } img.src = 'xxx.png' comsole.log('end' )
定时任务,如setTimeout callback hell 和 promise callback hell 1 2 3 4 5 6 7 8 9 10 11 12 13 $.get (url1, (data1)=>{ console .log(data1) $.get (url2, (data2)=>{ console .log(data2) $.get (url3, (data3)=>{ console .log(data3) ... } }) })
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 function getData ( ) { return new Promise ((resolve,reject )=> { $.ajax({ url, success(data){ resolve(data) }, error(err){ reject(err) } }) }) } const url1 = 'data1.json' const url2 = 'data2.json' const url3 = 'data3.json' getData(url1).then(data1 => { console .log(data1) return getData(url2) }).then(data2 => { console .log(data2) return getData(url3) }).then(data3 => { console .log(data3) }).catch(err => console .error(err)
手写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 const url = 'xxx.png' function loadImg (src ) { const p = new Promise ((resolve,reject )=> { const img = document .createElement('img' ) img.onload = () => { resolve(img) } img.onerror = () => { reject(new Erroe('图片加载失败' ))) } img.src = src }) return p } loadImg(url).then(img => { console .log(img.width) }).then(img => { console .log(img.height) }).catch(ex => { console .log(ex) }) const url2 = 'xxx2.png' loadImg(url1).then(img1 => { console .log(img1.width) return img1 }).then(img1 => { console .log(img1.height) return loadImg(url2) }).then(img2 => { console .log(img2.width) return img2 }).then(img2 => { console .log(img2.height) }).catch(ex => { console .log(ex) })