functionFoo(){getName=function(){console.log(1)}returnthis}Foo.getName=function(){console.log(2)}Foo.prototype.getName=function(){console.log(3)}// 在变量提升之后,此时5的函数会被4给替换,因为前面的getName在变量提升之后是5函数vargetName=function(){console.log(4)}functiongetName(){console.log(5)}// 请写出以下的输出结果Foo.getName()// 2getName()//4// 这里的执行顺序是先执行Foo函数,也就是(Foo()).getName(),// Foo执行后,函数中的getName没有变量修饰符,也就是会在全局变量中找,那么此时全局变量中的getName被1函数赋值了// Foo返回了一个this值,this指向window,最后的变成window.getName(),此时getName是全局函数,因此会执行,输出1Foo().getName()//1// 此时getName已经被修改了getName()//1// new (Foo.getName)() ==> new (function(){console.log(2);})() 会执行该函数并产生一个实例对象newFoo.getName()//2 // new Foo()是一个实例对象,此时类的原型对象上有一个getName函数,输出newFoo().getName()//3// new ((new Foo()).getName)() ==> new (function(){console.log(3);})() 执行该函数newnewFoo().getName()//3
functionNew(fn,...args){// 创建一个空的对象并链接到构造函数的原型,使它能访问原型中的属性const instance = object.create(fn.prototype)// 使用apply改变构造函数中this的指向实现继承,使obj能访问到构造函数中的属性const res =fn.apply(instance, args)// 优先返回构造函数返回的对象returntypeof res ==='object'||typeof res ==='function'? res : instance}functionPerson(name){this.name = name}Person.prototype.eat=function(){console.log('Eatting')}var lindaidai =New(Person,'LinDaiDai')console.log(lindaidai,'New')// Person{ name: 'LinDaiDai' }lindaidai.eat()// 'Eatting'
手写call、apply、bind
Function.prototype.Call=function(context,...args){if(!context) context = windowconst f =Symbol()context[f]=thisconst res = context[f](...args)delete context[f]return res}Function.prototype.Apply=function(context,...args){if(!context) context = windowconst f =Symbol()context[f]=thisconst res = context[f](args)delete context[f]return res}Function.prototype.Bind=function(context,...args){if(!context) context = windowconst f =Symbol()context[f]=thisreturnfunction(...args1){const res = context[f](...args,...agrs1)delete context[f]return res}}var obj ={name:'objName',}var name ='globalName'functionconsoleInfo(sex, weight){console.log(this.name, sex, weight,'this指向 call apply bind')}consoleInfo('man',100)// 'globalName' 'man' 100consoleInfo.Call(obj,'man',100)// 'objName' 'man' 100consoleInfo.Call(obj,'woman',120)// 'objName' 'woman' 120consoleInfo.Apply(obj,['man',100])// 'objName' 'man' 100consoleInfo.Apply(obj,['woman',120])// 'objName' 'woman' 120consoleInfo.Bind(obj,'man',100)()// 'objName' 'man' 100consoleInfo.Bind(obj,'woman',120)()// 'objName' 'woman' 120
相关题目
var a =10functionfoo(){// 默认模式下 函数的this执行windowconsole.log(this.a)// 10}foo()
'use strict'var a =10functionfoo(){// 严格模式下 函数的this 指向 undefinedconsole.log('this1',this)// undefinedconsole.log(window.a)// 10console.log(this.a)// 报错,undefined上没a}console.log(window.foo)// f foo(){...}console.log('this2',this)// windiowfoo()
// let const 声明的变量不存在变量提升 window下无 a,b 变量let a =10const b =20functionfoo(){console.log(this.a)// undefinedconsole.log(this.b)// undefined}foo()console.log(window.a)// undefined
// let const 声明的变量不存在变量提升 window下无 a,b 变量let a =10const b =20functionfoo(){console.log(this.a)// undefinedconsole.log(this.b)// undefined}foo()console.log(window.a)// undefined
var a =1functionfoo(){var a =2console.log(this)// windowconsole.log(this.a)// 1}foo()
var a =1functionfoo(){var a =2functioninner(){// 默认模式下,函数的this指向widowconsole.log(this.a)// 1}inner()}foo()
functionfoo(){console.log(this.a)}var obj ={ a:1}var a =2foo()// 2foo.call(obj)// 1foo.apply(obj)// 1foo.bind(obj)()// 1
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 =3obj2.foo1()// 2obj2.foo2()// window 3
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 =3obj2.foo1()// 2obj2.foo2()// obj1 1
var obj1 ={a:1,}var obj2 ={a:2,foo1:function(){console.log(this.a)},foo2:function(){functioninner(){console.log(this)console.log(this.a)}inner()},}var a =3obj2.foo1()// 2obj2.foo2()// window 3
functionfoo(){console.log(this.a)}var obj ={ a:1}var a =2foo()// 2foo.call(obj)// 1foo().call(obj)// 2, Cannot read property 'call' of undefined
functionfoo(){console.log(this.a)returnfunction(){console.log(this.a)}}var obj ={ a:1}var a =2foo()// 2foo.call(obj)// 1foo().call(obj)// 2 1
functionfoo(){console.log(this.a)returnfunction(){console.log(this.a)}}var obj ={ a:1}var a =2foo()// 2foo.bind(obj)foo().bind(obj)// 2
functionfoo(){console.log(this.a)returnfunction(){console.log(this.a)}}var obj ={ a:1}var a =2foo.call(obj)()// 1 2
var obj ={a:'obj',foo:function(){console.log('foo:',this.a)returnfunction(){console.log('inner:',this.a)}},}var a ='window'var obj2 ={ a:'obj2'}obj.foo()()// 'foo:obj' 'inner:window'obj.foo.call(obj2)()// 'foo:obj2' 'inner:window'obj.foo().call(obj2)// foo:'obj' 'inner:obj2'
var obj ={a:1,foo:function(b){b = b ||this.areturnfunction(c){console.log(this.a + b + c)}},}var a =2var obj2 ={ a:3}obj.foo(a).call(obj2,1)// 6 a = 3 b = 2 c = 1obj.foo.call(obj2)(1)// 6 a = 2 b = 3 c = 1
functionfoo1(){console.log(this.a)}var a =1var obj ={a:2,}varfoo2=function(){foo1.call(obj)}foo2()// 2foo2.call(window)// 2
functionfoo1(b){console.log(`${this.a} + ${b}`)returnthis.a + b}var a =1var obj ={a:2,}varfoo2=function(){returnfoo1.call(obj,...arguments)}var num =foo2(3)// 2 + 3console.log(num)// 5
var name ='window'functionPerson(name){this.name = namethis.foo=function(){console.log(this.name)returnfunction(){console.log(this.name)}}}var person2 ={name:'person2',foo:function(){console.log(this.name)returnfunction(){console.log(this.name)}},}var person1 =newPerson('person1')person1.foo()()// 'person1' windowperson2.foo()()// 'person2' window
var name ='window'functionPerson(name){this.name = namethis.foo=function(){console.log(this.name)returnfunction(){console.log(this.name)}}}var person1 =newPerson('person1')var person2 =newPerson('person2')person1.foo.call(person2)()// 'person2' windowperson1.foo().call(person2)// 'person1' '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()// windowobj.foo2()()// obj obj
var name ='window'var obj1 ={name:'obj1',foo:function(){console.log(this.name)},}var obj2 ={name:'obj2',foo:()=>{console.log(this.name)},}obj1.foo()// obj1obj2.foo()// window
var name ='window'var obj1 ={name:'obj1',foo:function(){console.log(this.name)returnfunction(){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)returnfunction(){console.log(this.name)}},}var obj4 ={name:'obj4',foo:()=>{console.log(this.name)return()=>{console.log(this.name)}},}obj1.foo()()// obj1 windowobj2.foo()()// obj2 obj2obj3.foo()()// window windowobj4.foo()()// window window
var name ='window'functionPerson(name){this.name = namethis.foo1=function(){console.log(this.name)}this.foo2=()=>{console.log(this.name)}}var person2 ={name:'person2',foo2:()=>{console.log(this.name)},}var person1 =newPerson('person1')person1.foo1()// person1person1.foo2()// person1person2.foo2()// window
var name ='window'functionPerson(name){this.name = namethis.foo1=function(){console.log(this.name)returnfunction(){console.log(this.name)}}this.foo2=function(){console.log(this.name)return()=>{console.log(this.name)}}this.foo3=()=>{console.log(this.name)returnfunction(){console.log(this.name)}}this.foo4=()=>{console.log(this.name)return()=>{console.log(this.name)}}}var person1 =newPerson('person1')person1.foo1()()// person1 windowperson1.foo2()()// person1 person1person1.foo3()()// person1 windowperson1.foo4()()// person1 person1
var name ='window'var obj1 ={name:'obj1',foo1:function(){console.log(this.name)return()=>{console.log(this.name)}},foo2:()=>{console.log(this.name)returnfunction(){console.log(this.name)}},}var obj2 ={name:'obj2',}obj1.foo1.call(obj2)()// obj2 obj2obj1.foo1().call(obj2)// obj1 obj1obj1.foo2.call(obj2)()// window windowobj1.foo2().call(obj2)// window obj2
var name ='window'var person1 ={name:'person1',foo1:function(){console.log(this.name)},foo2:()=> console.log(this.name),foo3:function(){returnfunction(){console.log(this.name)}},foo4:function(){return()=>{console.log(this.name)}},}var person2 ={ name:'person2'}person1.foo1()// person1person1.foo1.call(person2)// preson2person1.foo2()// windowperson1.foo2.call(person2)// windowperson1.foo3()()// windowperson1.foo3.call(person2)()// windowperson1.foo3().call(person2)// person2person1.foo4()()// person1person1.foo4.call(person2)()// person2person1.foo4().call(person2)// person1
var name ='window'functionPerson(name){this.name = namethis.foo1=function(){console.log(this.name)}this.foo2=()=> console.log(this.name)this.foo3=function(){returnfunction(){console.log(this.name)}}this.foo4=function(){return()=>{console.log(this.name)}}}var person1 =newPerson('person1')var person2 =newPerson('person2')person1.foo1()// person1person1.foo1.call(person2)// person2person1.foo2()// person1person1.foo2.call(person2)// person1person1.foo3()()// winodwperson1.foo3.call(person2)()// windowperson1.foo3().call(person2)// person2person1.foo4()()// person1person1.foo4.call(person2)()// person2person1.foo4().call(person2)// person1
var name ='window'functionPerson(name){this.name = namethis.obj ={name:'obj',foo1:function(){returnfunction(){console.log(this.name)}},foo2:function(){return()=>{console.log(this.name)}},}}var person1 =newPerson('person1')var person2 =newPerson('person2')person1.obj.foo1()()// windowperson1.obj.foo1.call(person2)()// windowperson1.obj.foo1().call(person2)// person2person1.obj.foo2()()// objperson1.obj.foo2.call(person2)()// person2person1.obj.foo2().call(person2)// obj
functionfoo(){console.log(this.a)// 2}var a =2;(function(){'use strict'foo()// 2, window调用})()
1) Class 类可以看作是构造函数的语法糖
2) Class 类中定义的方法,都是定义在该构造函数的原型上
3)使用static关键字,作为静态方法(静态方法,只能通过类调用,实例不能调用)
4)extents 关键字实际是寄生组合继承了避免与访问器属性冲突,在构造函数中使用了一个带有下划线前缀的私有属性_myProperty。这是一种常见的命名约定,用于表示该属性应该被视为私有的,以防止直接访问
functionFoo(){getName=function(){console.log(1)}returnthis}// 静态方法Foo.getName=function(){console.log(2)}// 成员方法Foo.prototype.getName=function(){console.log(3)}// 函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但是会被同名变量赋值后覆盖vargetName=function(){console.log(4)}functiongetName(){console.log(5)}//请写出以下输出结果:Foo.getName()// 2getName()// 4// Foo().getName(); // undefined is not a functiongetName()// 4newFoo.getName()// 2newFoo().getName()// 3newnewFoo().getName()// 3
Promise
手写promise
classMyPromise{constructor(execute){this.state ='pending'this.data =undefinedthis.error =undefinedthis.resolveTask =[]this.rejectTask =[]try{execute(this.resolve.bind(this),this.reject.bind(this))}catch(e){this.reject(e)}}resolve=(value)=>{if(this.state !=='pending')returnthis.state ='fulfilled'this.data = valuethis.resolveTask.forEach(cb=>cb())}reject=(error)=>{if(this.state !=='pending')returnthis.state ='rejected'this.error= errorthis.rejectTask .forEach(cb=>cb())}then=(onResolve, onReject)=>{onResolve =typeof onResolve ==='function'?onResolve:value=> valueonReject =typeof onReject ==='function'?onReject:(error)=>throw errorreturnnewMyPromise((resolve, reject)=>{this.resolveTask.push(()=>{const res =onResolve(this.data)if(res instanceofMyPromise){res.then(resolve, reject)}else{resolve(res)}})this.rejectTask.push(()=>{const res =onReject(this.error)if(res instanceofMyPromise){res.then(resolve, reject)}else{reject(res)}})})}catch=(onReject)=>{returnthis.then(undefined, onReject)}staticresolve=(value)=>{returnnewMyPromise((resolve, reject)=>{if(value instanceofMyPromise){value.then(resolve, reject)}else{resolve(value)}})}staticreject=(error)=>{returnnewMyPromise((resolve, reject)=>{if(value instanceofMyPromise){error.then(resolve, reject)}else{reject(error)}})}staticrace=(promises)=>{returnnewMyPromise((resolve, reject)=>{for(let i =0; i < promises.length; i++){MyPromise.resolve(promises[i]).then(value=>{resolve(value)},error=>{reject(error)})}})}staticall=(promises)=>{const result =[]let index =0returnnewMyPromise((resolve, reject)=>{for(let i =0; i < promises.length; i++){MyPromise.resolve(promises[i]).then(value=>{result[i]= valueindex++if(index === promises.length -1){resolve(resolve(value))}},error=>{reject(error)})}})}staticretry(fn, delay, times){returnnewMyPromise((resolve, reject)=>{functionfunc(){MyPromise.resolve(fn()).then((res)=>{resolve(res)}).catch((err)=>{// 接口失败后,判断剩余次数不为0时,继续重发if(times !==0){setTimeout(func, delay)times--}else{reject(err)}})}func()})}}// 打印结果:依次打印1、2newMyPromise((resolve, reject)=>{setTimeout(()=>{resolve(1)},500)}).then((res)=>{console.log(res)returnnewMyPromise((resolve)=>{setTimeout(()=>{resolve(2)},1000)})}).then((data)=>{console.log(data)})
渲染的基本流程:1、处理 HTML 标记并构建 DOM 树。2、处理 CSS 标记并构建 CSSOM 树, 将 DOM 与 CSSOM 合并成一个渲染树。3、根据渲染树来布局,以计算每个节点的几何信息。4、将各个节点绘制到屏幕上。可以看到渲染树的一个重要组成部分是CSSOM树,绘制会等待css样式全部加载完成才进行,所以css样式加载的快慢是首屏呈现快慢的关键点。
console.log('script start')asyncfunctionasync1(){awaitasync2()// await 隐式返回promiseconsole.log('async1 end')// 这里的执行时机:在执行微任务时执行}asyncfunctionasync2(){console.log('async2 end')// 这里是同步代码}async1()setTimeout(function(){console.log('setTimeout')},0)newPromise((resolve)=>{console.log('Promise')// 这里是同步代码resolve()}).then(function(){console.log('promise1')}).then(function(){console.log('promise2')})console.log('script end')/*** 首先同步任务先执行:script start 、遇到定时器,放到宏任务队列中,async1 start,遇到await,放入微任务队列中,执行async2,等待返回值async2* 后面的代码将在同步任务执行完之后再执行,继续执行promise,第一个函数仍然是同步代码,执行promise1,后面的函数放入微任务队列* 执行script end 同步任务执行完,执行异步微任务 async1 end、 promise2,这两者的顺序没有定论,看浏览器,最后执行宏任务setTimeout*/// 结果:script start --> async2 end --> Promise -->// script end --> async1 end --> promise1 --> promise2 --> setTimeout
数组操作
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'){thrownewTypeError(`${fn} is not a function`)}let pre, indexconst arr =this.slice()// 如果没有提供initValue,找到数组中的第一个存在的值作为pre,下一个元素的下标作为indexif(initValue ===undefined){for(let i =0; i < arr.length; i++){if(!arr.hasOwnProperty(i))continuepre = arr[i]index = i +1break}}else{// 如果提供了initValue时,则作为pre的初始值,index从0开始pre = initValueindex =0}for(let i = index; i < arr.length; i++){if(arr.hasOwnProperty(i)){// 函数接收4个参数 pre, cur, index, arraypre =fn.call(undefined, pre, arr[i], i,this)}}return pre}console.log([,,,1,2,3,4].Reduce((pre, cur)=> pre + cur))// 10
手写Map
Array.prototype.Map=function(fn, context){if(typeof fn !=='function'){thrownewTypeError(`${fn} is not a function`)}const arr =this.slice()const list =newArray(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))// [2, 4, 6]
手写filter
Array.prototype.Filter=function(fn, context){if(typeof fn !=='function'){thrownewTypeError(`${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))// [3, 4]
手写some
Array.prototype.Some=function(fn){if(typeof fn !=='function'){thrownewTypeError(`${fn} is not a function`)}let result =falsefor(let i =0; i <this.length; i++){if(fn(this[i], i)){result =truebreak}}return result}console.log([1,2,3,4].Some((item)=> item >6))// falseconsole.log([1,2,3,4].Some((item)=> item >2))// true
手写Every
Array.prototype.Every=function(fn){if(typeof fn !=='function'){thrownewTypeError(`${fn} is not a function`)}let result =falselet index =0for(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))// falseconsole.log([1,2,3,4].Every((item)=> item >0))// true
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 会将传入的值进行数字转换,任何不能进行数字转换的值都返回trueNumber.isNaN 会判断传入的值是否是数字,判断为数字再判断是不是NaN,不进行数据转换
undefined -> NaN null -> 0 true -> 1 false -> 0
'' -> 0 含有非数字的字符串 -> NaN
Symbol 不能转数字
转换到boolean
undfeined null +0 -0 '' NaN false 都为false 其余的逻辑上都为true
类型转换
// 当a等于什么的时候能使下面的条件成立var a =?if(a ==1&& a ==2&& a ==3){console.log(1);}/*** == 的转换规则* * 对象==字符串 对象.toStringnull==undefined 相等 但是和其他值不相等NaN!=NaN剩下的都转换成数字*/// 对象==字符串 对象.toString// 利用这个思想,将a写为一个对象,并且重写其toSrting方法,在第一次执行的时候返回1// 在第二次执行的时候返回2,第三次执行的时候返回3,使条件成立var a ={i:1,toString(){if(i =1){returnthis.i++}elseif(i =2){returnthis.i++}else{returnthis.i}}}// 利用Object.defineProperty进行数据劫持var i =0
Object.defineProperty(window,'a',{get(){return++i}})// 数组弹出var a =[1,2,3]
a.toString = a.shiftif(a ==1&& a ==2&& a ==3){console.log('成立')}
手写Typeof
functionTypeof(context){returnObject.prototype.toString.call(context).slice(8,-1).toLowerCase()}constfoo=()=>{}const str ='1'const boo =falseconst n =nullconst u =undefinedconst 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
functionInstanceof(context, fn){const proto = context.__proto__if(proto){if(proto === fn.prototype){returntrue}else{returnInstanceof(proto, fn)}}else{returnfalse}}constfoo=()=>{}const o =newObject()const a =newArray()const m =newMap()const w =newWeakMap()const s =newSet()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))