欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js基础问题

程序员文章站 2022-04-12 23:29:13
...

new做了什么?

1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此,this指向新对象)
3、执行构造函数(给新对象添加属性)
4、返回新对象

构造函数返回值有如下三种情况:

  • 1、返回一个对象, 新建的对象就是返回的对象
  • 2、没有 return,即返回 undefined,实例只能访问构造函数中的属性
  • 3、返回undefined 以外的基本类型,只能访问到构造函数中的属性,和情况1完全相反,结果相当于没有返回值
模拟一个new
function create() {
	// 创建一个空的对象
    var obj = new Object(),
	// 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
	// 链接到原型,obj 可以访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
	// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
	// 优先返回构造函数返回的对象
    return ret instanceof Object ? ret : obj;
};

Object.create与{}区别

1、使用Object.create()是将对象继承到__proto__属性上*
2、Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法
将{}点开,显示的是 No Properties ,也就是在对象本身不存在属性跟方法,原型链上也不存在属性和方法,
3、Object.create()方法接受两个参数:Object.create(obj,propertiesObject) ;
Obj:一个对象,应该是新创建的对象的原型
ropertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符
1、使用Object.create()是将对象继承到__proto__属性上*js基础问题
2、Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法
var test4 = Object.create(null);//console.log(test4.proto)=>undefined 没有继承原型属性和方法
js基础问题
3js基础问题
4js基础问题
js基础问题
对比发现,通过new Object比{}多了一些自定义的属性和方法
其实原型执项是一样的
js基础问题

instanceof

浅谈 instanceof 和 typeof 的实现原理 - 掘金
Obj instanceof Object
判断右侧变量的prototype是否出现在左侧变量的原型链上

function newInstance(left, right) {
            let leftProto = left.__proto__
            let rightProto = right.prototype
            while(true) {
                if (leftProto === null) return false
                if (leftProto === rightProto) return true
                leftProto = leftProto.__proto__
            }
        }

cookie属性

name字段:一个cookie的名称
value字段:一个cookie的值
domain字段:可以访问此cookie的域名
path字段:可以访问此cookie的页面路径
Size字段:此cookie大小
httponly字段:cookie的httponly属性,若此属性为True,则只有在http请求头中会有此cookie信息,而不能通过document.cookie来访问此cookie。
secure字段:设置是否只能通过https来传递此条cookie。
expires/Max-Age字段:设置cookie超时时间。如果设置的值为一个时间,则当到达该时间时此cookie失效。不设置的话默认是session,意思是cookie会和session一起失效,当浏览器关闭(并不是浏览器标签关闭,而是整个浏览器关闭)后,cookie失效。
document.cookie = encodeURIComponent(sKey) + “=“ + encodeURIComponent(sValue) + sExpires + (sDomain ? “; domain=“ + sDomain : “”) + (sPath ? “; path=“ + sPath : “”) + (bSecure ? “; secure” : “”);

cookie以及session

看完这篇 Session、Cookie、Token,和面试官扯皮就没问题了 - 掘金
js基础问题
js基础问题

模块化进展

前端模块化详解(完整版) - 掘金
这几个概念你可能还是没搞清require、import和export - 掘金

js基础问题

1、全局function模式 : 将不同的功能封装成不同的全局函数

  • 编码: 将不同的功能封装成不同的全局函数
  • 问题: 污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系
    namespace模式 : 简单对象封装
  • 作用: 减少了全局变量,解决命名冲突
  • 问题: 数据不安全(外部可以直接修改模块内部的数据)
  • 这样的写法会暴露所有模块成员,内部状态可以被外部改写。
let myModule = {
  data: 'www.baidu.com',
  foo() {
    console.log(`foo() ${this.data}`)
  },
  bar() {
    console.log(`bar() ${this.data}`)
  }
}
myModule.data = 'other data' //能直接修改模块内部的数据
myModule.foo() // foo() other data

IIFE模式:匿名函数自调用(闭包)

  • 作用: 数据是私有的, 外部只能通过暴露的方法操作
  • 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口
  • 问题: 如果当前这个模块依赖另一个模块怎么办?
// module.js文件
(function(window) {
  let data = 'www.baidu.com'
  //操作数据的函数
  function foo() {
    //用于暴露有函数
    console.log(`foo() ${data}`)
  }
  function bar() {
    //用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }
  function otherFun() {
    //内部私有的函数
    console.log('otherFun()')
  }
  //暴露行为
  window.myModule = { foo, bar } //ES6写法
})(window)

IIFE模式增强 : 引入依赖

这个必须先引入依赖库,再把库当做参数传入,容易依赖关系导致加载先后顺序出错。

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操作数据的函数
  function foo() {
    //用于暴露有函数
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }
  function otherFun() {
    //内部私有的函数
    console.log('otherFun()')
  }
  //暴露行为
  window.myModule = { foo, bar }
})(window, jQuery)

模块化的好处

  • 避免命名冲突(减少命名空间污染)
  • 更好的分离, 按需加载
  • 更高复用性
  • 高可维护性

Require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错

CommonJs,是在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理
特点:

  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。

AMD与CMD

CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。
CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。
AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案

common.js 和 es6区别

https://github.com/mqyqingfeng/frontend-interview-question-and-answer/issues/5

JavaScript 模块的循环加载 - 阮一峰的网络日志
JS模块化的杂七杂八 - 掘金
前端模块化:CommonJS,AMD,CMD,ES6 - 掘金

CommonJS暴露出的是一个值的拷贝,一旦暴露接口,这个接口在模块内部的变化是监听不到的;ES6 Module暴露的是内容的引用,模块内部被暴露的接口改变会影响引用的改变
若遇到重复加载的情况,CommonJS会直接从第一次加载时生成对象的exports属性中取值;ES6 Module则会通过引用找到模块暴露接口的内存位置,并从中取值
若出现循环加载情况,CommonJS只输出已经执行的部分,还未执行的部分不会输出;ES6 Module需要开发者自己保证,真正取值的时候能够取到值
CommonJS是加载时执行,若出现循环加载情况,则从已执行的内容中取值;ES6 Module是动态引用,加载模块时不执行代码,只是生成一个指向被加载模块的引用
CommonJS模块是运行时加载,ES6 Module模块是编译时输出接口
CommonJS是加载整个模块,ES6 Module可以按需加载部分接口

文档碎片

文档碎片的apendChild具有移动性
把别的地方的节点移动到文档碎片里面,别的就不存在了

正则

正则的惰性匹配与贪婪匹配的差别

惰性的话,有多短匹配多短
贪婪,有多长匹配多长
惰性匹配: 在量词后面加个问号就能实现惰性匹配,场景
js基础问题

正则的RegExp会自动保存上次匹配的结果

https://blog.csdn.net/qq_42423964/article/details/102385983?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

(?=p)和(?!p)

(?=p),其中p是一个子模式,即p前面的位置。

var result = “hello”.replace(/(?=l)/g, ‘#’);
console.log(result);
// => “he#l#lo”

而(?!p)就是(?=p)的反面意思

var result = “hello”.replace(/(?!l)/g, ‘#’);

console.log(result);
// => “#h#ell#o#”
::特点:从后向前查找::

async处理错误

如果在async函数中抛出了错误,则终止错误结果,不会继续向下执行。*
Eg:

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

运行结果:
‘async2’
Uncaught (in promise) error

如果改为throw new Error也是一样的:

async function async1 () {
  console.log('async1');
  throw new Error('error!!!')
  return 'async1 success'
}
async1().then(res => console.log(res))

运行结果:
‘async1’
Uncaught (in promise) Error: error!!!

try catch

async function async1 () {
  try {
    await Promise.reject('error!!!')
  } catch(e) {
    console.log(e)
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

运行结果:
‘script start’
‘error!!!’
‘async1’
‘async1 success’
或者你可以直接在Promise.reject后面跟着一个catch()方法:

async function async1 () {
  // try {
  //   await Promise.reject('error!!!')
  // } catch(e) {
  //   console.log(e)
  // }
  await Promise.reject('error!!!')
    .catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

运行结果是一样的。

websocket

在一个单独的持久链接上实现双向通信。与服务器进行全双工、双向通信的信道。使用自定义的协议而非HTTP,专门为快速传输小数据设计。

defer与async区别

浅谈script标签中的async和defer - 贾顺名 - 博客园
必须明白的浏览器渲染机制 - 掘金
Script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了。
直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染。

js基础问题
js基础问题

闭包

就是指有权访问另一个函数作用域中的变量的函数* 闭包
js基础问题
这里可以看一下lazyMan的那个面试题,非常之经典呀,还有防抖与节流,都是关于闭包的

数组

哪些改变数组自身的方法?
pop、push、shift、unshift、reverse、sort、fill、splice

slice与splice

1. slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

var array=[1,2,3,4,5,6];
var arr1=array.slice(0,3); // 输出[1,2,3]
var arr2=array.slice(3); //如果 end 被省略,则 slice 会一直提取到原数组末尾,输出[4,5,6]。
var arr3=array.slice(-1); //如果只传入一个参数,且是负数时,length会与参数相加,然后再截取,输出[6]
var arr4=array.slice(2,-3);  //如果当传入两个参数一正一负时,length也会先于负数相加后,再截取,输出[3]
var arr5=array.slice(-8);  //如果只传入一个参数,是负数时,并且参数的绝对值大于数组length时,会截取整个数组,输出[1,2,3,4,5,6]
var array=arr.slice(8);  //如果传入一个参数,大于length时,将返回一个空数组,输出[]


2、splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

3. split() 方法使用指定的分隔符字符串将一个String对象分割成子字符串数组,以一个指定的分割字串来决定每个拆分的位置。 //这个不是数组的方法呢

数组实例的 flat(),flatMap()
该方法返回一个新数组,对原数据没有影响。
flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
如果原数组有空位,flat()方法会跳过空位。
flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
[2, 3, 4].flatMap((x) => [x, x * 2])
数组实例的 includes()
方法返回一个布尔值,注意,可以查找是否包括NaN
*数组实例的 entries(),keys() 和 values() *
ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for…of循环进行遍历

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

数组实例的 fill()
fill(值,开始位置,结束位置) //不包括结束位置

*数组实例的 find() 和 findIndex() *
直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。也可以用于找NaN,之前的indexOf方法就不支持

[1, 4, -5, 10].find((n) => n < 0)
// -5
[NaN].findIndex(y => Object.is(NaN, y))
// 0

Array.of()
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

普通函数与箭头函数的区别

详解箭头函数和普通函数的区别以及箭头函数的注意事项、不适用场景 - 掘金

  • 箭头函数没有prototype(原型),所以箭头函数本身没有this
  • 箭头函数的this在定义的时候继承自外层第一个普通函数的this。
  • 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)
  • 箭头函数本身的this指向不能改变,但可以修改它要继承的对象的this。
  • 箭头函数的this指向全局,使用arguments会报未声明的错误。
  • 箭头函数的this指向普通函数时,它的argumens继承于该普通函数
  • 使用new调用箭头函数会报错,因为箭头函数没有constructor
  • 箭头函数不支持new.target
  • 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
  • 箭头函数相对于普通函数语法更简洁优雅

拓展:
js基础问题

继承

多种继承以及各自优缺点,网上太多了

call、apply以及bind的区别

//在非严格模式下
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window

//严格模式下
fn.call();//在严格模式下this->undefined
fn.call(null);// 在严格模式 下this->null
fn.call(undefined);//在严格模式下this->undefined

Call以及apply在严格模式下和非严格模式下对于第一个参数是null/undefined这种情况的规律也是一样的。

区别:
call和apply直接执行函数,而bind需要再一次调用。
Bind只是改变了函数中的this为obj,并且给函数传递了两个参数值,但是此时并没有把这个函数执行
call与bind第二个参数开始接受一个参数列表,apply第二个参数开始接受一个参数数组

Function.prototype.myApply = function(thisArg, args) {
    const fn = Symbol('fn')        // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
    thisArg = thisArg || window    // 若没有传入this, 默认绑定window对象
    thisArg[fn] = this              // this指向调用call的对象,即我们要改变this指向的函数
    const result = thisArg[fn](...args)  // 执行当前函数
    delete thisArg[fn]              // 删除我们声明的fn属性
    return result                  // 返回函数执行结果
}

//测试
foo.myApply(obj, []) // 输出’写代码像蔡徐抻’

使用JSON.Stringfy拷贝对象的缺点

https://www.jianshu.com/p/52db1d0c1780

1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,symbol,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;

基本数据类型

number、string、number、undefined、boolean、symbol、bigInt
number的表达范围## 2^53 - 1
BigInt 可以表示任意大的整数
BigInt字面量:BigInt(123456)
BigInt表达式:123456n

拓展一下symbol:

symbol,值是独一无二的,可以用于解决命名冲突,保证不会与其他属性名产生冲突;
Symbol的作用非常的专一,换句话说其设计出来就只有一个目的——作为对象属性的唯一标识符,防止对象属性冲突发生。
Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要用于区分不同的 Symbol 变量;
Symbol 作为属性名,不会被常规方法遍历得到,即该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,但是,它并不是私有属性,可以使用 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名;
js基础问题

ready、onload执行顺序

document.ready 和 window.onload 的区别是:上面定义的document.ready方法在DOM树加载完成后就会执行,而window.onload是在页面资源(比如图片和媒体资源,它们的加载速度远慢于DOM的加载速度)加载完成之后才执行。也就是说$(document).ready要比window.onload先执行。

document本身是没有reay方法的,需要我们自己去自定义
js基础问题

target、currentTarget

(事件冒泡、事件捕获)
E.target指向引发触发事件的元素,只具体点击到的元素
而e.currentTarget指向的是给绑定事件监听的那个对象

EventLoop(事件循环)

  1. 首先 JavaScript 引擎会执行一个宏任务,注意这个宏任务一般是指主干代码本身,也就是目前的同步代码
  2. 执行过程中如果遇到微任务,就把它添加到微任务任务队列中
  3. 宏任务执行完成后,立即执行当前微任务队列中的微任务,直到微任务队列被清空
  4. 微任务执行完成后,开始执行下一个宏任务
  5. 如此循环往复,直到宏任务和微任务被清空
    宏任务(macro-task): 同步 script (整体代码),setTimeout 回调函数, setInterval 回调函数, I/O, UI rendering;
    微任务(micro-task): process.nextTick, Promise 回调函数,Object.observe,MutationObserver
    js基础问题

async await

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理) - 掘金
通过上面的分析,我们知道async隐式返回 Promise 作为结果的函数,那么可以简单理解为,await后面的函数执行完毕时,await会产生一个微任务(Promise.then是微任务)。但是我们要注意这个微任务产生的时机,它是执行完await之后,直接跳出async函数,执行其他代码(此处就是协程的运作,A暂停执行,控制权交给B)。其他代码执行完毕后,再回到async函数去执行剩下的代码,然后把await后面的代码注册到微任务队列当中。我们来看个例子:
https://juejin.im/post/5dc28ea66fb9a04a881d1ac0

防抖与节流

防抖:

function debounce(fn, time) {
            let timer = null
            return function() {
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments)
                }, time)
            }

        }

节流:

// 节流,每隔一段时间执行一次
        function throttle(func, waiter) {
            let timer = null
            return function() {
                if (!timer) {
                    timer = setTimeout(() => {
                        // clearTimeout(timer) 不能这么写,因为会把之后的定时器的内容给清空掉
                        timer = null
                        func.apply(this, arguments)
                    }, waiter)
                }
            }
        }

使用

function sayHi() {
    console.log('-----say hi')
}
document.onmousemove = throttle(sayHi, 1000)

== 隐式类型转化

从一道面试题说起—js隐式转换踩坑合集 - 掘金
ToPrimitive指对象类型类型(如:对象、数组)转换为原始类型的操作。
当对象类型需要被转为原始类型时,它会先查找对象的valueOf方法,如果valueOf方法返回原始类型的值,则ToPrimitive的结果就是这个值
如果valueOf不存在或者valueOf方法返回的不是原始类型的值,就会尝试调用对象的toString方法,也就是会遵循对象的ToString规则,然后使用toString的返回值作为ToPrimitive的结果。

注意:对于不同类型的对象来说,ToPrimitive的规则有所不同,比如Date对象会先调用toString,具体可以参考 ECMA标准

宽松相等(==)比较时的隐式转换规则

2.1 布尔类型和其他类型的相等比较
只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型
根据布尔类型的toNumber规则,true转为1,false转为0

2.2 数字类型和字符串类型的相等比较
当数字类型# 和字符串类型做相等比较时,字符串类型会被转换为数字类型
根据字符串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN
这里就不讨论NaN了,因为NaN和任何值都不相等,包括它自己。
2.3对象类型和原始类型的相等比较
当对象类型# 和原始类型# 做相等比较时,对象类型# 会依照ToPrimitive# 规转换为原始类型
ToNumber的时候,如果valueOf和toString都没有返回原始类型的值,则会抛出异常。
2.4 null、undefined和其他类型的比较

Null和undefined宽松相等的结果为true,这一点大家都知道
其次,null# 和undefined# 都是假值,那么

  null == false // false
  undefined == false // false
首先,false转为0,然后呢, null与undefined会被转换为数字Nan

ToPrimitive会先查找对象的valueOf方法,如果返回的是原始类型,就停止,否则,调用对象的toString方法
其中valueOf与toString
对象: valueOf() 返回对象本身,toString() 返回值为 [object Object]。
数组: valueOf() 返回对象本身,数组改写了 toString(),返回值相当于用 join(‘,’) 的返回值,比如 [1,2,3].toString() 返回 “1,2,3”。
方法: valueOf() 返回方法本身,Function 也改写了对象的 toString(),它将代码转为字符串值然后返回。
行首出现{},javascript引擎会当作代码块解析,而不是作为一个空对象来解析,可通过为行首的{}添加括号,({})会当成空对象来处理

{} + [] == 0
其实{}只是一个代码块
{} + [] //''
{} + 1 // 1
[] + {} == {} + []
解析为 "[object Object]" == "[object Object]"
const a = {
    // 定义一个属性来做累加
    i: 1,
    valueOf () {
      return this.i++
    }
  }
  a == 1 && a == 2 && a == 3 // true

this的指向判读

【建议????】再来40道this面试题酸爽继续(1.2w字用手整理) - 掘金
JavaScript:面试频繁出现的几个易错点 - 掘金
1、如果是箭头函数,由于箭头函数没有 this ,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this
箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window
2、如果使用bind、call、apply,this执行第一个参数
3、如果是普通函数,那么,如果使用了new,this指向这个创建的对象,如果不是,哪个对象调用这个函数,this就指向哪个对象

4、还有一个 对于直接调用 调用函数(如 foo() ) 来说,不管 foo 函数被放在了什么地方,this 非严格模式下一定是 window,严格模式是undefine
5、自执行函数中的this永远是window

函数调用方式:
普通函数调用
apply和call调用
箭头函数调用
对象函数调用
构造函数调用注意:在构造函数里面返回一个对象,会直接返回这个对象,而不是执行构造函数后创建的对象

setTimeout中的this传给 setTimeout 的是普通函数, this 指向是 window
setTimeout 里面的this 指向window;

箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window

 <button id="btn1">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    <script type="text/javascript">   
        let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                btn1.onclick = () => {
                    console.log(this);//obj
                };
            }
        };
        obj.getName();
    </script>

 <button id="btn1">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    <script type="text/javascript">   
        let btn2 = document.getElementById('btn2');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: () => {
                btn2.onclick = () => {
                    console.log(this);//window
                };
            }
        };
        obj.getName();
    </script>

上例中,虽然存在两个箭头函数,其实this取决于最外层的箭头函数,由于obj是个对象而非函数,所以this指向为Window对象
由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        return fn.call({birth:2000}, year);
    }
};
obj.getAge(2018); // 28

promise

有三种状态,并且状态之间是不可逆的

promise.all

手写这一部分
.catch()函数能够捕获到.all()里最先的那个异常,并且只执行一次。

promise.finally

  1. .finally()方法不管Promise对象最后的状态如何都会执行
  2. .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected的
  3. 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。

Promise.resolve(‘1’) .then(res => { console.log(res) }) .finally(() => { console.log(‘finally’) }) Promise.resolve(‘2’) .finally(() => { console.log(‘finally2’) return ‘我是finally2返回的值’ }) .then(res => { console.log(‘finally2后面的then函数’, res) })

‘1’
‘finally2’
‘finally’
‘finally2后面的then函数’ ‘2’

相关标签: 面试问题