js基础问题

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__属性上*在这里插入图片描述
2、Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法
var test4 = Object.create(null);//console.log(test4.proto)=>undefined 没有继承原型属性和方法
在这里插入图片描述
3在这里插入图片描述
4在这里插入图片描述
在这里插入图片描述
对比发现,通过new Object比{}多了一些自定义的属性和方法
其实原型执项是一样的
在这里插入图片描述

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,和面试官扯皮就没问题了 - 掘金
在这里插入图片描述
在这里插入图片描述

模块化进展

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

在这里插入图片描述

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具有移动性
把别的地方的节点移动到文档碎片里面,别的就不存在了

正则

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

惰性的话,有多短匹配多短
贪婪,有多长匹配多长
惰性匹配: 在量词后面加个问号就能实现惰性匹配,场景
在这里插入图片描述

正则的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渲染。

在这里插入图片描述
在这里插入图片描述

闭包

就是指有权访问另一个函数作用域中的变量的函数* 闭包
在这里插入图片描述
这里可以看一下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
  • 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
  • 箭头函数相对于普通函数语法更简洁优雅

拓展:
在这里插入图片描述

继承

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

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 属性名;
在这里插入图片描述

ready、onload执行顺序

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

document本身是没有reay方法的,需要我们自己去自定义
在这里插入图片描述

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
    在这里插入图片描述

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’

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值