基本数据类型有哪些?null 是对象吗?基本类型和复杂类型存储有什么区别?
查看详情
- 基本数据类型有:undefined, null, boolean, string, number, symbol, BigInt(第三阶段)
- null 不是对象,但是 typeof null 将返回 Object
- 基本类型存储在栈内存,存储的是值。复杂类型的值存储在堆内存,地址存储在栈内存。
undefined 和 null 的区别是什么?
查看详情
- undefined:未定义的值,表示一个变量最原始的状态,而非人为操作的结果,出现的场景:
- 声明了一个变量但是没有赋值
- 访问对象上不存在的属性
- 函数定义了形参,但没有传递实参
- 使用 void 对表达式求值
- null:空值,表示一个对象被人为的重置为空对象,而非一个变量最原始的状态,在内存中表示就是栈中的变量没有指向堆内存对象,出现场景:
- 手动指定一个 null
- 原型链的终点
说出以下代码的执行结果
javascript
function Person(name, age) {
this.name = name;
this.age = age;
}
function fn(person) {
person.name = '李四';
person = new Person('王五', 20);
}
var p = new Person('张三', 13);
console.log(p.name);
fn(p);
console.log(p.name);
查看详情
张三
李四
将'get-element-by-id'装换为'getElementById'
查看详情
javascript
const foo = 'get-element-by-id';
// 1
const str = foo
.split('-')
.map((word, outerIndex) => {
if (outerIndex === 0) return word;
return [...word]
.map((character, innerIndex) => {
if (innerIndex === 0) return character.toUpperCase();
return character;
})
.join('');
})
.join('');
// 2
const arr = foo.split('-');
for (let i = 1; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1);
}
const str2 = arr.join('');
谈谈对 Symbol 的理解
查看详情
- Symbol 是一种新的数据类型,表示独一无二的值
- Symbol 没有字面量语法,创建它的唯一方法是使用构造函数创建
- Symbol 函数前不能使用 new 命令
- Symbol 函数接受一个字符串作为参数表示 Symbol 实例的描述
- 如果参数是一个对象就会调用该对象的 toString 方法将其转为字符串然后生成一个 Symbol 值
- 对象的属性名现在可以有两种类型:字符串和 Symbol 类型
- Symbol 值不能与其他的类型的值进行运算,会报错
- Symbol 值可以显式的转为字符串和布尔值,但不能转为数值
为什么 0.1 + 0.2 != 0.3?如何解决这个问题?
查看详情
- 在计算前首先会将十进制数转为二进制数,而 ECMAScript 中的 Number 类型遵循 IEEE 754 标准,采用 64 位双精度浮点数的方式表示,一个浮点数可以表示为等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。对于 64 位双精度浮点数,第一位为符号位,接下来 11 位是指数偏移值,剩下 52 位为有分数值。由于分数值总是以 1.xxx 开头的,所以只会存储小数点往后的 52 位,分数值第 53 位及以后的数字不能存储,如果是 1 就向前进一位,是 0 就舍弃。所以在某些不能精确转换为二进制的数字(例如:0.1)在进行计算的时候会发生精度丢失从而影响计算结果。
- 如何解决:
- 将浮点数放大为100倍(或者其他倍数)然后在进行计算,最后在缩小100倍
- 使用toFixed对计算结果四舍五入后再使用parseFloat
- 第三方计算库
为什么最大安全数字是 2^53-1 不是 2^52-1
查看详情
ECMAScript 中的 Number 类型遵循 IEEE 754 标准,采用 64 位双精度浮点数的方式表示,一个浮点数可以表示为等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。对于 64 位双精度浮点数,第一位为符号位,接下来 11 位是指数偏移值,剩下 52 位为有分数值。由于分数值总是以 1.xxx 开头的,所以只会存储小数点往后的 52 位,分数值第 53 位及以后的数字不能存储,如果是 1 就向前进一位,是 0 就舍弃。也就是说计算机中存储的分数值最大为 52 个 1 加上小数点前的 1 一共 53 个 1 也就是 2^53-1
取数组的最大值
查看详情
javascript
const arr = [6, 5, 78, 10, 4];
// 1
Math.max.apply(null, arr);
Math.max.call(null, ...arr);
// 2
Math.max(...arr);
// 3
// array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
// reduce接受一个函数作为累加器,将数组中每个值(从左到右)开始缩减,最终计算为一个值
arr.reduce((max, current) => {
return (max = max > current ? max : current);
});
typeof 是否正确判断类型?instanceof 呢?它的实现原理是什么?
查看详情
- typeof 能够判断除了 null 以外的基本数据类型,但对于对象来说不起作用。
- instanceof 可以准却判断复杂数据类型,但不能判断基本数据类型。
- instanceof 是通过原型链判断,A instanceof B, 在 A 的原型链中层层查找,是否有原型等于 B.prototype, 如果一直找到 A 的原型链的顶端(null;即
Object.__proto__.__proto__
),仍然不等于 B.prototype,那么返回 false,否则返回 true.
for...of, for...in, forEach, map 的区别
查看详情
- for...of 循环:具有 Iterator 接口,就可以用 for...of 遍历它的成员(属性)。例如,数组、Set、Map、类似数组的对象、Generator 对象以及字符串。对于普通对象不能直接使用 for...of,必须部署了 Iterator 接口才能使用。可以中断循环。
- for...in 循环:遍历对象自身的和继承的可枚举的属性,不能直接获得属性值。可以中断循环。
- forEach:只能遍历数组。不能中断。无返回值。
- map:只能遍历数组。不能中断。返回值是修改后的数组。
如何判断一个变量是不是数组
查看详情
- Array.isArray(), 返回 true/false
- Object.prototype.toString.call(), 返回
"[object Array]"
- instanceof, 返回 true/false
arr.__proto__ === Array.prototype
javascript
function fn() {
console.log(Array.isArray(arguments)); //false; 因为arguments是类数组,但不是数组
console.log(Array.isArray([1, 2, 3, 4])); //true
console.log(arguments instanceof Array); //fasle
console.log([1, 2, 3, 4] instanceof Array); //true
console.log(Object.prototype.toString.call(arguments)); //[object Arguments]
console.log(Object.prototype.toString.call([1, 2, 3, 4])); //[object Array]
console.log(arguments.constructor === Array); //false
arguments.constructor = Array;
console.log(arguments.constructor === Array); //true
console.log(Array.isArray(arguments)); //false
}
fn(1, 2, 3, 4);
类数组和数组的区别是什么?
查看详情
类数组拥有 length 属性,但不具有数组所具有的的方法。类数组是一个普通的对象,而真实的数组是 Array 类型。常见的类数组有:函数的参数 arguments,DOM 对象列表(比如通过 document.querySelectorAll 得到的列表),jQuery 对象(如$("div"))。类数组可以通过以下方式转为数组:
- Array.prototype.slice.call(arrayLike)
- [...arrayLike]
- Array.from(arrayLike)
== 和 === 有什么区别
查看详情
- === 不需要进行类型转换,只有类型相同并且值相等时才返回 true
- == 如果两者类型不同,首先需要进行类型转换,具体流程如下:
- 判断两者类型是否相同,如果相等,判断值是否相等
- 如果类型不同,进行类型转换
- 判断比较的是否是 null 或者 undefined,如果是,返回 true
- 判断两者类型是否为 string 和 number,如果是,将字符串转为 number 在比较
- 判断一方是否为 Boolean,如果是,将 Boolean 转为 number 在进行判断
- 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,如果是,将 object 转为与另一方类型相同的原始类型在进行判断
javascript
null == undefined; // true
1 == '1'; // true
1 == '1ab'; // false
true == '0'; // false
[] == ![]
查看详情
- !的优先级高于==
- 引用类型转为布尔值都是 true,所以![]是 false,等于在比较[] == false
- 比较一方为布尔值时会将 Boolean 转为数字,所以[] == 0
- 比较一方为对象,另一方为字符串、数字或者 symbol 时,会将对象转为与另一方类型相同的原始类型,所以是 0 == 0(空数组转换成数字,对应的值是 0,如果数组中只有一个数字,那么转成 number 就是这个数字,其它情况,均为 NaN),返回 true
数组的那些 API 会改变原数组?
查看详情
- 会改变原数组的 API:
- copyWithin(targetIndex, start, ?end): 从数组的指定位置(start, ?end)拷贝元素到当前数组的另一个指定位置(targetIndex)中,返回新数组
- fill(value, ?start, ?end): 用 value 替换数组中的值,返回新数组
- pop(): 删除数组的最后一个元素并返回删除的元素
- push(item1, ?item2, ..., ?itemX): 向数组的末尾添加一个或多个元素,并返回新的长度
- reverse(): 方法用于颠倒数组中元素的顺序,返回颠倒后的数组
- shift(): 删除并返回数组的第一个元素
- slice(?start, ?end): 从已有的数组中返回选定的元素,无参数时将返回原数组
- sort(?sortFunction): (按照排序函数)对数组元素进行排序,返回排序后的数组
- splice(index, ?howMany, ?item1,....., ?itemX): 用于添加或删除数组中的元素,如果是删除元素返回被删除元素组成的数组,否则返回空数组
- unshift(?item1, ?item2, ..., ?itemX): 向数组的开头添加一个或更多元素,并返回新的长度
- 不会改变原数组的 API:
- concat(arr1, arr2, ...): 将 arr1,arr2...连接在一起,返回新的数组
- filter(function(currentValue, ?index, ?arr), ?thisValue): 创建一个新的数组,将符合条件的元素放入新数组,返回这个新数组
- find(function(currentValue, ?index, ?arr), ?thisValue): 为数组中的每一个元素执行一次函数,返回第一个符合条件的值,并终止函数执行,没有返回 undefined
- findIndex(function(currentValue, ?index, ?arr), ?thisValue): 为数组中的每一个元素执行一次函数,返回第一个符合条件的值的索引,并终止函数执行,没有返回-1
- forEach(function(currentValue, ?index, ?arr), ?thisValue): 为数组中的每一个元素执行一次函数,无返回值
- includes(searchElement, ?fromIndex): 判断一个数组中是否包含指定值,返回 true 或 false
- indexOf(item, ?start): 查找指定值的索引
- lastIndexOf(item, ?start): 查找指定值的索引,从后往前搜索
- join(separator): 把数组通过分隔符转为字符串,返回字符串
- map(function(currentValue, ?index, ?arr), ?thisValue): 为数组中的每一个元素执行方法,返回执行后的数组
- every(function(currentValue, ?index, ?arr), ?thisValue): 检测数组所有元素是否都符合指定条件,所有元素都通过返回 true,否则返回 false
- some(function(currentValue, ?index, ?arr), ?thisValue): 检测数组中的元素是否满足指定条件(函数提供),只要一个元素满足就返回 true,剩下的不会再执行
- reduce(function(total, currentValue, ?currentIndex, ?arr), ?initialValue): 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值,返回计算结果
- reduceRight(function(total, currentValue, ?currentIndex, ?arr), ?initialValue): 接收一个函数作为累加器,数组中的每个值(从右到左)开始缩减,最终计算为一个值,返回计算结果
- toString(): 将数组转为字符串
- 其他方法
- Array.from(object, ?mapFunction, ?thisValue): 用拥有 length 属性的对象或可迭代的对象来返回数组
- Array.isArray(obj): 判断一个对象是否为数组,返回 true/false
var, let 和 const 的区别是什么?
查看详情
- var 创建的变量会出现变量提升,而 let 和 const 创建的变量不会出现变量提升
- let 和 const 为 JS 新增了块级作用域
- let 和 const 不允许重复声明,而 var 允许重复声明
- 在 let 和 const 声明之前使用会抛出异常(形成了暂时性死区),而 var 不会
- 用 const 声明的常量,在声明后不能更改,如果声明一个对象则不能更改对象的引用地址
Object.freeze()和 const 的区别是什么?
查看详情
- const 声明一个只读的变量,一旦声明就不能改变它的值。对于对象可以添加、修改、删除其属性
- Object.freeze()适用于对象值,它使对象不可变,也不能添加、改变、删除其属性(严格模式会报错)
实现 a, b 两个变量值的交换
查看详情
javascript
let a = 1,
b = 2;
// 1
let temp = a;
a = b;
b = temp;
// 2
a = a + b;
b = a - b;
a = a - b;
// 3
b = [a, (a = b)][0]
// 4
[a, b] = [b, a];
// 5
a ^= b;
b ^= a;
a ^= b;
什么是变量提升?什么是暂时性死区?
查看详情
- 变量提升:在使用var声明变量,变量声明之前就能使用,值为 undefined
- 暂时性死区:在代码块内使用 let/const 命令声明变量之前,该变量都是不可用的(只要进入当前的作用域,所要使用的变量就已经存在了,但是不能获取,只有等到声明变量的那一行代码出现才能使用,否则会报错),这在语法上称为暂时性死区。这也意味着 typeof 操作符不是一个百分百安全的操作符。
['1', '2', '3'].map(parseInt)
查看详情
javascript
['1', '2', '3'].map(function (currentValue, currentIndex, array) {
return parseInt(currentValue, currentIndex)
})
结果: [1, NaN, NaN]
- map 函数回调函数的参数
array.map(function callback(currentValue, currentIndex, array){...}, thisArg)
- currentValue 当前遍历的值
- currentValue 当前遍历的值的索引
- array 原数组
- thisArg 执行 callback 函数时 this 指向的对象
- parseInt 用来解析字符串
parseInt(string, radix)
- string 被处理的字符串,
- radix 解析时的基数,取值在 2~36 之间的整数,默认为 10
- 当 radix 为 undefined/0/null/没有指定时。string 以‘0x’开头则基数为 16;其他情况基数是 10
- 当 radix 为 1 时,无法解析,会返回 NaN
- 当 radix 为其他值时,基数为对应值
javascript
['1', '2', '3'].map(parseInt)[
// 等价于 [parseInt('1', 0), parseInt('2', 1), parseInt('3', 2)]
// parseInt('1', 0) => 1
// parseInt('2', 1) => NaN
// parseInt('3', 2) => NaN 无法用二进制(基数2)表示'3', 所以是NaN
// 结果[1, NaN, NaN]
('10', '10', '10', '10', '10')
].map(parseInt);
// 等价于 [parseInt('10', 0), parseInt('10', 1), parseInt('10', 2), parseInt('10', 3), parseInt('10', 4),]
// 结果[10, NaN, 2, 3, 4]
['1', '2', '3'].map(parseFloat)
查看详情
- parseFloat 函数可解析一个字符串,并返回一个浮点数
parseFloat(string)
,该函数指定的字符串的首个字符是否为数字,如果是则对字符串进行解析, 知道到达数字的末端为止,然后返回该数字。否则返回 NaN。
javascript
['1', '2', '3'].map(parseFloat);
// [1, 2, 3]
['1', '2', '3'].filter(parseInt)
查看详情
['1']
['1', '2', '3'].filter(parseFloat)
查看详情
['1', '2', '3']
Set, Map, WeakSet 和 WeakMap 的区别
查看详情
- Set 是一种叫做集合的的数据结构,它类似于数组,但是成员不可重复。Set 本身是一个构造函数,用来生成 Set 数据结构。
- 向 Set 加入值的时候值不会发生发生类型转化。Set 内部通过一种类似于严格等于的方式判断两个值是否相等,但认为 NaN 是等于自身的。
- Set 实例的属性有 size(表示实例的元素个数)和 constructor。Set 实例的方法 add(), delete(), has()和 clear(), 以及遍历方法 keys(), values(), entries()和 forEach()。
- Set 默认可遍历,默认迭代器生成函数时 values()方法
- WeakSet 结构与 Set 类似,成员不可重复。但成员只能是对象,而且 WeakSet 对象中存储的对象都是弱引用。WeakSet 实例的属性有 constructor。方法有 add(), has()和 delete()。WeakSet 不可遍历。
- Map 是一种叫做字典的数据结构,类似于对象,但是键名不只是字符串,也可以是其他的类型。
- Map 内部通过一种类似于严格等于的方式判断两个键名是否相等,NaN 等于自身。
- Map 实例的属性有 size(表示实例的元素个数)。方法有 set(), get(), has(), delete()和 clear(), 以及遍历方法 keys(), values(), entries()和 forEach()。
- Map 默认可遍历,默认遍历器就是 entries()方法
- WeakMap 结构类似于 Map,但是键名只能是对象,且键名是弱引用。WeakMap 实例的属性有 constructor。方法有 has(), get(), set()和 delete()。
Object.is()与比较操作符===,==有什么区别
查看详情
- Object.is()类似于===,但有一些细微的差别
- NaN 和 NaN 相等
- -0 和+0 不相等
- == 只会判断值是否相等
JS 类型转换的规则是什么?
查看详情
- 显式转化
- Number()
- parseInt()
- parseFloat()
- toString()
- String()
- Boolean()
- 隐式转换
- 逻辑运算符(&&、||、!)
- 运算符(+、-、*、/)
- 关系运算符(>、>=、<、<=)
- 相等运算符(==)
- if/while
什么是深拷贝?浅拷贝?赋值?它们的区别是什么?
查看详情
- 深拷贝和浅拷贝只是针对引用数据类型的,对于引用数据类型其值存储在堆内存,栈内存只是存储的是指针,该指针指向堆中该实体的地址
- 深拷贝对源对象中的所有子对象进行递归拷贝,拷贝前后两个对象不互相影响
- 浅拷贝重新在堆中创建内存,只拷贝源对象的一层,不能对源对象中的子对象进行拷贝(如:Object.assign(), 展开运算符等)
- 赋值只是复制指向对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
可迭代对象的特点是什么?
查看详情
- 知道如何访问集合中的一项,并跟踪该序列中的当前位置
- 提供 next()方法,用来返回数据结构当前成员的信息,包含 value 和 done 两个属性对象,value 为当前成员的值,done 用来表示是否迭代完成
- 迭代器一旦被创建,就可以反复调用 next()
数据排序
查看详情
- sort
javascript
// 升序
[5, 4, 9, 2, 7, 3, 1]
.sort((a, b) => a - b)
// 降序
[5, 4, 9, 2, 7, 3, 1].sort((a, b) => b - a);
- 冒泡排序
javascript
// 升序
function bubbleSort(data) {
var temp = 0;
for (var i = data.length; i > 0; i--) {
for (var j = 0; j < i - 1; j++) {
// 降序改为小于号
if (data[j] > data[j + 1]) {
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
return data;
}
如何自定义实现一个unshift方法
查看详情
javascript
Array.prototype.myUnshift = function () {
const length = arguments.length;
for (let i = length - 1; i >= 0; i--) {
const element = arguments[i];
this.splice(0, 0, element);
}
return this.length;
}
获取指定范围内的随机数
查看详情
javascript
function fn(min, max) {
if (min >= max) return;
// (min, max)
// return Math.round(Math.random() * (max - min - 2) + min + 1);
// [min, max]
// return Math.round(Math.random() * (max - min) + min);
// (min, max]
// return Math.ceil(Math.random() * (max - min) + min);
// [min, max)
// return Math.floor(Math.random() * (max - min) + min);
}