前端面试基础题:JavaScript变量声明、装箱和拆箱…

2023-07-0309:08:19WEB前端开发Comments964 views字数 15499阅读模式

✨ script标签defer和async

前端面试基础题:JavaScript变量声明、装箱和拆箱…
  • <script>:如果遇到script标签,会阻塞HTML的解析,等到script下载执行完成之后再继续解析HTML。
  • <script defer>:script的加载不会阻塞HTML的解析,等到HTML解析完成再按照加载顺序执行脚本。
  • <script async>:script的加载不会阻塞HTML的解析,脚本加载完成就立即执行,并且是乱序执行的。

asyncdefer具有更高的优先级,如果同时添加两个属性,则会忽略defer文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ 变量声明

关键字varletconst
作用域函数作用域块级作用域块级作用域
变量提升存在不存在不存在
能否修改(改变指针指向)
初始值可以不设置初始值可以不设置初始值必须设置初始值
是否添加到全局属性
能否重复声明变量
  • 变量提升:浏览器会默认把带有var声明的变量在内存中进行提前声明或定义
  • 暂时性死区:用let或const声明的变量在声明之前都会被放到暂时性死区,在此时访问这些变量就会触发运行时错误
  • 块级作用域:就是使用一对大括号包裹的一段代码,比如函数、判断语句、循环语句,甚至单独的一个{}都可以被看作是一个块级作用域
  • 全局属性:在全局作用域下,var成为 window 对象的属性, let和const不会
  • Array和Object都是引用类型,当用const声明数组和对象时,const声明的常量保存的仅仅是目标的指针,这就意味着只要保证数组和对象的指针不发生改变,修改其中的值是被允许的。
  • for循环中var和let的区别
for (var i = 0; i < 5; i++) {
  console.log(i);
}
// 输出:0 1 2 3 4

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
// 输出:5 5 5 5 5

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
// 输出:0 1 2 3 4

由于var的变量提升机制,var命令实际只会执行一次,作用于全局,随着循环不断改变变量的值,且setTimeout是异步任务,等到执行时变量已经改变为循环最后一次的值。而let命令不存在变量提升,所以每次循环都会执行一次,声明一个新变量(但初始化的值不同),for的每次循环都是不同的块级作用域,let声明的变量也是块级作用域,所以也不存在重复声明的问题,let声明变量的for循环里,每个匿名函数实际上引用的都是一个新的变量。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ 数据类型

前端面试基础题:JavaScript变量声明、装箱和拆箱…
  • 基本数据类型的数据直接存储在中,而引用类型的数据存储在中,在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。
  • 栈内存是自动分配的,堆内存是开发者动态分配的,每次使用完对象的时候需要把它设置为null,从而减少无用的内存消耗,或程序结束时由垃圾回收机制回收。

✨ Number类型

  1. 八进制:前面加0,十六进制:前面加0x
  2. 范围:Number.MAX_VALUE(1.797631348623157e+308),Number.MIN_VALUE(5e-32)
  3. 最大安全整数:Number.MAX_SAFE_INTEGER( 253−1 )
前端面试基础题:JavaScript变量声明、装箱和拆箱…
位置位数作用表示
0-5152尾数位原码表示
52-6211指数位移码表示
631符号位0,1

“安全”意思是说能够one-by-one表示的整数,也就是说在 (−253,253) 范围内,双精度数表示和整数是一对一的,反过来说,在这个范围以内,所有的整数都有唯一的浮点数表示,这叫做安全整数。在 253−1 ​之后的数中,只要指数相同,并且尾数前52位相同,则这个两个数数值相同。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

4. 三个特殊值:Infinity,-Infinity,NaN(通过isFinite()、isNaN()判断)
5. NaN不等于任何值,包括自身文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ String类型

方法功能
str.indexOf(要查找的字符串,开始位置)返回指定内容在字符串中的位置,找不到返回-1
str.lastIndexOf(要查找的字符串)从后往前找,只找第一个匹配的
str.charAt(index)返回指定位置的字符
str.charCodeAt(index)获取指定位置处字符的ASCII码
str.concat(str1, str2, ...)用于连接两个或多个字符串
str.substr(start, length)从start位置开始,返回length长的子串
str.slice(start, end)返回从start位置开始到end位置的子串,end取不到
str.substring(start, end)返回从start位置开始到end位置的子串,end取不到,但不接受负值
str.replace(被替换字符串,要替换的字符串)用于在字符串中用一些字符替换另一些字符
str.toUpperCase()将字符串转换为大写
str.toLowerCase()将字符串转换为小写
str.trim()去除字符串两端的空白字符

模板字符串:反引号定义,${}插入变量或函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

手写模板字符串文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

function render(template, data) {
    const reg = /\$\{(\w+)\}/;
    if (reg.test(template)) {
        const key = reg.exec(template)[1];
        template = template.replace(reg, data[key]);
        return render(template, data);
    }
    return template;
}

✨ undefined和null

  • null == undefined 为 true
  • null === undefined 为 false
表达式结果表达式结果
Number(null)0Number(undefined)NaN
Boolean(null)falseBoolean(undefined)false
String(null)nullString(undefined)undefined

区别文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  1. 含义不同:undefined表示未定义的值,null表示一个空对象,变量声明了但还没有定义的时候会返回 undefinednull主要用于赋值给一些可能会返回对象的变量,作为初始化。
  2. 类型不同:typeof undefined为Undefined,typeof null为Object
  3. 数字转换结果不同:Number(undefined)为NaN,Number(null)为0

如何获取安全的 undefined 值?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

因为 undefined 是一个标识符,所以可以被当作变量来使用和赋值,但是这样会影响 undefined 的正常判断。表达式 void ___ 没有返回值,因此返回结果是 undefined。void 并不改变表达式的结果,只是让表达式不返回值。因此可以用 void 0 来获得 undefined。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ Symbol类型(ES6)

  • Symbol类型表示独一无二的值,通过Symbol()函数生成,可以用来防止对象属性名冲突(属性名可以是Symbol类型或String类型)。
  • Symbol()函数前不能使用new命令,否则会报错。这是因为Symbol是一个原始类型的值,不是对象,所以不能使用new命令来调用。另外由于Symbol值不是对象,所以也不能添加属性。
  • Symbol()函数可以传入一个字符串作为参数,便于区分。如果传入的参数是一个对象,会调用对象的toString方法。
  • Symbol值不能与其他类型的值进行运算,会报错。
  • Symbol值可以显式转换为字符串或布尔类型,不能转为数值。
let sym = Symbol();
console.log(typeof sym); // symbol

let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');

console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false

console.log(genericSymbol); // Symbol()
console.log(fooSymbol); // Symbol(foo)

✨ BigInt类型(ES6)

一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  • 要创建BigInt,只需在整数的末尾追加n即可。或者,可以调用BigInt()构造函数。
  • 不能使用严格相等运算符将BigInt与常规数字进行比较,因为它们的类型不同。可以使用等号运算符,它在处理操作数之前执行隐式类型转换。

✨ Object类型

创建对象的三种方式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

// 方式1:字面量
var star = {
    name: 'pink',
    age: 18,
    sex: '男',
    sayHi() { alert("Hi~"); }
}

// 方式2:Object构造函数
var andy = new Object();
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi() = function () { alert("Hi~"); }

// 方式3:自定义构造函数
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayHi = function () { alert("Hi~"); }
}
var bigBai = Person('大白', 100, '男');

遍历对象属性文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

for (var k in obj) {
    console.log(k);
    console.log(obj[k]);
}

对象方法文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

方法功能
Object.keys(obj)获取对象自身所有属性,类似for...in
Object.values(obj)获取对象自身所有属性值
Object.assign(target, source)拷贝一个对象的属性值到目标对象中(重复的键会被覆盖,浅拷贝)
Object.create(obj)创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
Object.entries(obj)返回给定对象自身可枚举属性的键值对数组
Object.freeze(obj)冻结一个对象,被冻结后的对象不能够修改
Object.hasOwnProperty(key)用来检测是否为对象的自有属性,对象原型上的属性为false
Object.defineProperty(obj, prop, descriptor)定义对象中新属性或修改原有属性

Object.defineProperty方法参数:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

obj:目标对象
prop:属性名
descriptor:目标属性所拥有的特性,用对象格式写 ​文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  • value:设置属性的值,默认undefined
  • writable:值是否可以重写,默认false ​
  • enumerable:目标属性是否可以被枚举,默认false ​
  • configurable:目标属性是否可以被删除或再次修改属性,默认false
  • get: 属性的getter函数,当访问该属性时会调用此函数
  • set: 属性的setter函数,当属性值被修改时,会调用此函数,方法会接收一个默认参数(被赋予的新值)

new创建一个实例对象的过程文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  1. 创建一个空对象
  2. 继承构造函数的原型
  3. this指向obj,并调用构造函数
  4. 返回对象
function myNew(fn, ...args) {
    const obj = {};
    obj.__proto__ = fn.prototype;
    fn.apply(obj, args);
    return obj;
}

✨ Array类型

方法功能
Array.isArray()判断一个对象是否为数组
arr.push(arg1, arg2, ...)数组末尾添加一个或多个元素,并返回新数组长度
arr.pop()删除数组中最后一个元素,返回删除的元素的值
arr.unshift(arg1, arg2, ...)数组开头添加一个或多个元素,并返回新数组长度
arr.shift()删除数组的第一个元素,并返回删除的元素的值
arr.reverse()颠倒数组中元素的顺序,修改原数组,返回新数组
arr.sort((a, b) => { return a - b; }) //升序对数组元素进行排序,修改原数组,返回新数组
arr.indexOf(item)在数组中查找给定元素的第一个索引,不存在返回-1
arr.lastIndexOf(item)在数组中查找给定元素的最后一个索引,不存在返回-1
arr.join('分隔符')将数组中的所有元素转换为一个以分隔符分隔的字符串
arr.concat(arr1, arr2, ...)连接两个或多个数组,不影响原数组,返回一个新数组
arr.slice(start, end)返回被截取元素的新数组[start, end)
arr.splice(start, length)返回被删除元素的新数组,原数组中删除这几个元素
arr.forEach((item, index, arr) => {})对数组进行遍历循环处理,没有返回值,通过抛出异常跳出循环,通过return跳过当次循环
arr.map((item, index, arr) => {})返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,不会改变原数组
arr.filter((item, index, arr) => {})数组中的每一项运行给定函数,返回满足过滤条件组成的数组
arr.fill(num[, start, end])使用特定值填充数组中的一个或多个元素。当只是用一个参数时,该方法会用该参数的值填充整个数组。3个参数: 填充数值,起始位置参数,结束位置参数(不包括结束位置的那个元素)
arr.every((item, index, arr) => {})判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true
arr.some((item, index, arr) => {})判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true
arr.includes(num)判断一个数组是否包含一个指定的值,如果是返回true,否则 false
arr.reduce((prev, item, index, arr) => {}, initVal)实现迭代数组的所有项,然后构建一个最终返回的值
arr.find((item, index, arr) => {})返回第一个满足条件的元素值
arr.findIndex((item, index, arr) => {})返回第一个满足条件的元素索引
Array.from(arrayLike)将类数组或可遍历对象转换为真正的数组

reduce函数的应用场景:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  1. 计算数组中所有值的总和/最值
const arr = [1, 3, 5, 6, 4, 2];
// 总和
let tolValue = arr.reduce((prev, item) => {
  return prev + item;
}, 0);
// 最值
let maxValue = arr.reduce((prev, item) => {
  return Math.max(prev, item);
}, Number.MIN_VALUE);

2. 数组去重文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
const quchong = (arr) => {
  let res = [];
  arr.reduce((prev, item) => {
    if (!prev.has(item)) {
      prev.set(item, 1);
      res.push(item);
    }
    return prev;
  }, new Map());
  return res;
}
console.log(quchong(arr));  // [1, 2, 3, 4, 5]

3. compose函数的实现文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const f1 = x => x + 1;
const f2 = x => x + 2;
const f3 = x => x + 3;
const f4 = x => x + 4;
function compose(...fns) {
    if (fns.length === 0) return x => x;
    if (fns.length === 1) return fns[0];
    return fns.reduce((prev, next) => {
        return (num) => prev(next(num));
    })
}
console.log(compose(f1, f2, f3, f4)(1));

4. 二维数组转化为一维文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const arr = [[1, 2], [3, 4], [5, 6]];
let flattened = arr.reduce((prev, item) => {
    return prev.concat(item);
}, []);

5. 计算数组中每个元素出现的次数文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const votes = ['vue', 'react', 'angular', 'vue', 'react', 'vue'];
let count = votes.reduce((prev, item) => {
  if (!prev[item]) {
    prev[item] = 1;
  } else {
    prev[item]++;
  }
  return prev;
}, {});

✨ RegExp类型

创建正则表达式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

var 变量名 = new RegExp(/表达式/);
var 变量名 = /表达式/;

测试正则表达式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

reg.test(str);  // 用来测试某个字符串是否与正则匹配,匹配就返回true,否则返回false
reg.exec(str);  // 返回的是一个数组,数组中第0个元素是匹配的子字符串,第1个元素是正则中的第一个子分组匹配的结果...
str.match(reg);  // 是String对象的方法

三种测试方法的对比:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const template = '我叫{{name}},今年{{age}}岁。'
const reg = /\{\{(\w+)\}\}/;

// test方法
let res_test = reg.test(template);
console.log(res_test);  // true

// exec方法
let res_exec = reg.exec(template);
console.log(res_exec);
// [
//   '{{name}}',
//   'name',
//   index: 2,
//   input: '我叫{{name}},今年{{age}}岁。',
//   groups: undefined
// ]

// match方法
let res_match = template.match(reg);
console.log(res_match);
// [
//   '{{name}}',
//   'name',
//   index: 2,
//   input: '我叫{{name}},今年{{age}}岁。',
//   groups: undefined
// ]

正则表达式语法文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

前端面试基础题:JavaScript变量声明、装箱和拆箱…

其他语法拓展:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

    • \b\B
      \b匹配单词边界,\B元字符匹配非单词边界。不同则为界,界左和界右肯定不是相同类型。
前端面试基础题:JavaScript变量声明、装箱和拆箱…
前端面试基础题:JavaScript变量声明、装箱和拆箱…
    • ?=n 和?!n
      ?=n 匹配任何其后紧接指定字符串 n 的字符串,?!n匹配任何其后没有紧接指定字符串 n 的字符串。
const str = 'happy happily';
const pattn1 = /happ(?=ily)/;
const pattn2 = /happ(?!ily)/;
console.log(pattn1.exec(str));
// 匹配happily [ 'happ', index: 6, input: 'happy happily', groups: undefined ]
console.log(pattn2.exec(str));
// 匹配happy [ 'happ', index: 0, input: 'happy happily', groups: undefined ]
    • 回溯引用
      所谓回溯引用(backreference)指的是模式的后面部分引用前面已经匹配到的子字符串。你可以把它想象成是变量,回溯引用的语法像\1,\2,....,其中\1表示引用的第一个子表达式,\2表示引用的第二个子表达式,以此类推。而\0则表示整个表达式。
// 匹配两个连续相同的单词
const str = 'Hello what what is the first thing, and I am am scq000.';
const pattn = /\b(\w+)\s\1/;
console.log(pattn.exec(str));
// [
//   'what what',
//   'what',
//   index: 6,
//   input: 'Hello what what is the first thing, and I am am scq000.',
//   groups: undefined
// ]

✨ Date类型

创建实例文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

new Date()
new Date(milliseconds)
new Date(dateString)
new Date(year, month, day, hours, minutes, seconds)

获取毫秒形式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

date.valueOf()
date.getTime()
Date.now()
+new Date()

方法文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

函数作用
getFullYear()获取当年
getMonth()获取当月(0-11)
getDate()获取当天日期
getDay()获取星期几(周日0—周六6)
getHours()获取当前小时
getMinutes()获取当前分钟
getSeconds()获取当前秒钟

✨ Math类型

函数作用
Math.PI圆周率
Math.floor() / Math.ceil()向下/上取整
Math.round()四舍五入
Math.abs()绝对值
Math.min() / Math.max()最小/大值
Math.random()返回[0,1)范围内随机一个小数
Math.sqrt() / Math.pow(base, exp)平方根/基数base的exp次幂

✨ Function类型

  • 声明函数
function funcName (arg1, arg2, ) {}
var fn = function () {};  // 匿名函数
() => {}  // 箭头函数
(function () {}) ();  // 立即执行函数
(function () {} ());  // 立即执行函数
  • JS中没有函数重载,后定义的会覆盖先定义的
  • return只能返回一个值,如果用逗号隔开多个值,以最后一个为准
  • arguments是一个伪数组,存储了函数中传递进来的所有实参。伪数组不能调用数组的方法。
  • 函数的length属性返回的是希望接收的参数个数。
  • 箭头函数不会提升,函数表达式归类于变量声明,享受变量提升,函数声明的优先级高于变量声明
  • 作用域链:根据在内部函数能够访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问就称为作用域链
  • 箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this

✨ Set类型

类似于数组,但成员都是唯一的,没有重复的值文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

实例方法作用
add(value)添加某个值,返回Set结构本身
delete(value)删除某个值,返回一个布尔值,表示删除是否成功
has(value)返回一个布尔值,表示该值是否为Set成员
clear()清除所有成员,没有返回值

✨ Map类型

方法作用
set(key, value)设置Map对象键的值
get(key)根据键获取Map对象的值
delete(key)根据键删除Map对象的键值对
clear()清空Map对象
has(key)根据键查看Map对象中是否有该键

Map类型和Object类型的区别文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

MapObject
键类型Map的键可以是任意值,包括函数、对象或任意基本类型Object 的键必须是 String 或是Symbol
键的顺序Map中的键是有序的(插入的顺序)Object中的键是无序的
创建方法只能通过new创建可以通过字面量,自定义构造函数或new创建
访问元素方式map.get(key)obj.key或obj[key]
新增元素或修改元素值map.set(key, val)obj[key] = val或obj.key = val
删除元素map.delete(key)delete obj[key]或delete obj.key
键值对数量map.sizeObject.keys(obj).length
迭代支持不支持

✨ 深拷贝和浅拷贝

  • 深拷贝:每一层都拷贝了,改变数据不会影响原对象
    • JSON.stringfyJSON.parse
  • 浅拷贝:只拷贝第一层,深层的依然是引用,改变深层会影响原对象
    • Object.assign()

手写深拷贝文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

function isObj(item) {
    // typeof 会将 Array 也判断为 Object
    // 还要排除空对象的可能
    return typeof item === 'object' && item !== null;
}

function deepClone(obj) {
    // 判断要拷贝的对象是数组还是对象
    const newObj = obj instanceof Array? []: {};
    for (const key in obj) {
        const item = obj[key];
        // 简单数据类型只需要赋值,如果是复杂数据类型需要递归进行深拷贝
        newObj[key] = isObj(item)? deepClone(item): item;
    }
    return newObj;
}

✨ 数据类型检测

  1. typeof

typeof null的值为Object无法区分是null还是object(因为在JavaScript中,不同的对象都是使用二进制存储的,如果二进制前三位都是0的话,系统会判断为是Object类型,而null的二进制全是0,自然也就判断为Object),除function的复杂数据类型均判断为object无法区分。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

typeof 1 // number
typeof '1'  // string
typeof undefined  // 'undefined'
typeof true  // 'boolean'
typeof Symbol()  // 'symbol'
typeof null  // 'object'
typeof []  // 'object'
typeof {}  // 'object'
typeof new Date()  // 'object'
typeof console.log;  // 'function'

2. instanceof文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

只能判断某对象是否存在于目标对象的原型链上(基本数据类型通过字面量赋值无法用instanceof检测出正确结果)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str1 = 'hello world'
str1 instanceof String // false

var str2 = new String('hello world')
str2 instanceof String // true

手写instanceof文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

function myInstanceof(L, R) {  // L代表instanceof左边,R代表右边
    var RP = R.prototype;  // 构造函数原型(显式原型)
    var LP = L.__proto__;  // 对象原型(隐式原型)
    while (true) {
        if (LP === null) return false;
        if (LP === RP) return true;
        LP = LP.__proto__;
    }
}

3. constructor文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

constructor并不可靠,容易被修改文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

var d = new Number(1)
var e = 1
function fn() {
  console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;

console.log(e.constructor);//ƒ Number() { [native code] }
console.log(e.constructor.name);//Number
console.log(fn.constructor.name) // Function 
console.log(date.constructor.name)// Date 
console.log(arr.constructor.name) // Array 
console.log(reg.constructor.name) // RegExp

4. Object.prototype.toString.call()文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

一种最好的基本类型检测方式, Object.prototype.toString() 会返回 [object, [[class]]] 的字符串,其中 [[class]] 会返回es定义的对象类型,包含"Arguments", “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”, 和 “String”;再加上es5新增加的返回 [object Undefined]和[object Null]文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" 
console.log(Object.prototype.toString.call(null)); // "[object Null]" 
console.log(Object.prototype.toString.call(123)); // "[object Number]" 
console.log(Object.prototype.toString.call("abc")); // "[object String]" 
console.log(Object.prototype.toString.call(true)); // "[object Boolean]" 
function fn() {
  console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(Object.prototype.toString.call(fn));// "[object Function]" 
console.log(Object.prototype.toString.call(date));// "[object Date]" 
console.log(Object.prototype.toString.call(arr)); // "[object Array]"
console.log(Object.prototype.toString.call(reg));// "[object RegExp]"

Object对象本身就有一个toString()方法,返回的是当前对象的字符串形式,原型上的toString()返回的才是我们真正需要的包含对象数据类型的字符串。由于Object.prototype.toString()本身允许被修改,像Array、Boolean、Number的toString就被重写过,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ 数据类型转换

前端面试基础题:JavaScript变量声明、装箱和拆箱…
  • nullundefined都没有toString()方法,但String()可以作用于nullundefined返回”null””undefined”
  • 只有数字型的toString()方法能够接收一个可选参数,用来指定计算时的基数,parseInt()也可以指定进制数
  • Boolean([]) 和 Booean({}) 都会转换成true
  • [] == ![]:[]转换为数字0,![]首先转换为布尔值,由于[]作为一个引用类型,布尔值为true,因此![]为false。再转换为数字,变为0。最终的结果为true
  • [].toString():'',{}.toString():[object Object]

✨ 运算符优先级

运算符优先级从高到低如下表所示:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

优先级运算符
16()分组
15x++, x--后置递增、后置递减
14!逻辑非,~按位非,+x, -x一元加法、一元减法
++x, --x前置递增、前置递减
13**幂
12*乘法,/除法,%取余
11+加法,-减法
10<<, >>按位左移、按位右移,>>>无符号右移
9<, >小于、大于,<=, >=小于等于,大于等于
8==, !=相等、不相等,===, !==严格相等,严格不相等
7&按位与
6^按位异或
5|按位或
4&&逻辑与
3||逻辑或
2x? a: b条件三元运算符
1=, +=, -=, **=, *=, /=, %=, <<=, >>=, >>>=,
&=, ^=, |=, &&=, ||=赋值运算符号

✨ || 和 && 操作符的返回值?

|| 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。
  • && 则相反,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。

|| 和 && 返回它们其中一个操作数的值,而非条件判断的结果文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ 位运算

位运算符文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

位运算符运算规则
& 按位与1&1=1, 1&0=0&1=0, 0&0=0
| 按位或1|1=1, 1|0=0|1=1, 0|0=0
^ 按位异或1^1=0, 1^0=0^1=1, 0^0=0
~ 取反~1=0, ~0=1
<< 左移右边补0,左边丢弃
>> 右移正数左补0,负数左补1,右边丢弃
>>> 无符号右移左边补0,右边丢弃

原码、反码和补码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  1. 原码:原码就是一个数的二进制数。
  2. 反码:正数的反码与原码相同,负数的反码为除符号位,按位取反。
  3. 补码:正数的补码与原码相同,负数的补码是原码除符号位外的所有位取反即0变1,1变0,然后加1,也就是反码加1。

✨ isNaN和Number.isNaN的区别

  • isNaN:除了判断NaNtrue外,还会把不能转成数字的判断为true,比如'abc'
  • Number.isNaN:只会判断NaNtrue

✨ 0.1+0.2>0.3

在JS底层每个变量是以二进制表示的,固定长度为64位,其中第一位为符号位,再往后11位是指数位,最后52表示的是尾数位,而0.1和0.2转为二进制的时候是无限循环小数,所以JS就会进行截取(最大安全数字),截取以后0.1和0.2就不是他们本身了,要比原来大一些,所以0.1+0.2>0.3。 解决方法:设置一个误差范围:Math.abs(arg1 - arg2) < Number.EPSILON,(Number.EPSILON为)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

整数二进制用数值乘以2的幂次依次相加,小数二进制用数值乘以2的负幂次依次相加。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

0.1101→​ 2−1+2−2+2−4=0.8125文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

✨ Object.is和===的区别

Object.is在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0-0NaNNaN文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

表达式结果表达式结果
+0 === -0trueObject.is(+0, -0)false
NaN === NaNfalseObject.is(NaN, NaN)true
function is(x, y) {
    if (x === y) {
        // 1 / +0 = +Infinity, 1 / -0 = -Infinity是不一样的
        return x !== 0 || y !== 0 || 1 / x === 1 / y;
    } 
    else {
        return x !== x && y !== y;
    }
}

✨ ==和===有什么区别

  1. ===是严格意义上的相等,会比较两边的数据类型和值大小
    数据类型不同返回false
    数据类型相同,但值大小不同,返回false
  2. ==是非严格意义上的相等
    两边类型相同,比较大小
    两边类型不同,先转换数据类型,再进一步进行比较。
  • Null == Undefined → true

✨ 装箱和拆箱

  • 装箱:将基本数据类型转换为对应的引用数据类型的操作,装箱又分为显式装箱和隐式装箱
    • 显式装箱:通过内置对象或基本包装类型对基本数据类型进行操作
      (三个基本包装类型:Boolean、Number、String)
const name = new String('Uni');
console.log(name.length);  // 3
    • 隐式装箱:创建一个对应类型实例 → 在实例中调用需要的方法或属性 → 销毁这个实例
const name = 'Uni';
console.log(name.length);  // 3

// 内部执行过程
let newName = new String(name);
console.log(newName.length);
newName = null;
  • 拆箱:将引用数据类型转换为对应的基本数据类型的操作(通过valueOf方法)
const bool = new Boolean(false);
console.log(bool);  // [Boolean: false]
console.log(bool.valueOf());  // false

console.log(!bool);  // false
console.log(!bool.valueOf());  // true

✨ ToPrimitive算法

JavaScript 对象转换到基本类型值时,会使用 ToPrimitive算法,这是一个内部算法,是编程语言在内部执行时遵循的一套规则。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

ToPrimitive算法在执行时,会被传递一个参数 hint,表示这是一个什么类型的运算(也可以叫运算的期望值),根据这个 hint 参数,ToPrimitive算法来决定内部的执行逻辑。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

hint 参数的取值只能是下列 3 者之一:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  • string
  • number
  • default

hint 值为 "string" 时,先调用 toStringtoString 如果返回一个基本类型值了,则返回、终止运算;否则接着调用 valueOf 方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

否则,先调用 valueOfvalueOf 如果返回一个基本类型值了,则返回、终止运算;否则接着调用 toString 方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/49417.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/49417.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定