50个JavaScript「基础」知识点
原文作者:林三心不学挖掘机
原文网址:https://mp.weixin.qq.com/s/-VLnevAo5jay_8t_9vvWhg
下面的这些题目是我根据原文的答案和自己的理解相互结合的
1、JavaScript有几种数据类型?
一共有8种数据类型,前面7种是基本数据类型,最后一种是复杂数据类型
- number:数字类型
- string:字符串类型
- boolean:布尔值类型
- undefined:未定义类型
- null:空类型
- symbol:symbol类型
- bigint:大数字类型
- object:对象类型
2、JavaScript最大安全数字与最小安全数字?
console.log(Number.MAX_SAFE_INTEGER)
// 9007199254740991
console.log(Number.MIN_SAFE_INTEGER)
// -9007199254740991
3、深拷贝与浅拷贝的区别?
- 浅拷贝:只拷贝第一层,深层的依然是引用,改变深层会影响原对象
- 深拷贝:每一层都拷贝了,改变数据不会影响原对象
具体如何实现深浅拷贝可以看之前写的文章
4、闭包是什么?
闭包是一个函数,是一个能让外部访问到函数内部的函数
- 优点:使外部能访问内部,延长内部变量寿命
- 缺点:滥用闭包造成内存泄漏
function a () {let num = 0// 这是个闭包return function () {return ++num}
}
const b = a()
console.log(b()) // 1
console.log(b()) // 2
5、原型链是什么呀?
原型链是一条引用的链,实例的隐式原型指向构造函数的显式原型,可以使用A instanceof B
来判断B是否在A的原型链上。
6、什么是变量提升?函数提升?
-
变量提升
console.log(name) // undefined var name = 'Sunshine_Lin'if (false) {var age = 23 } console.log(age) // undefined 不会报错
函数提升:
console.log(fun) // function fun() {} function fun() {}if (false) {function fun2(){} } console.log(fun2) // undefined 不会报错
-
函数提升优先级 > 变量提升优先级
-
console.log(fun) // function fun() {} var fun = 'Sunshie_Lin' function fun() {} console.log(fun) // 'Sunshie_Lin'
-
7.isNaN 与 Number.isNaN的区别?
isNaN
:除了判断NaN为true外,还会把不能转成数字判断为true,例如’dasd’Number.isNaN
:只会判断NaN为true
8、解决遍历对象时,把原型上的属性遍历出来了咋办?
注意 遍历对象的时候使用for-in循环,for-of和for-each是用来遍历数组的,for-in循环遍历对象除symbol类型外的可枚举属性,包括继承得到的
使用hasOwnProperty
判断
function Person(name) {this.name = name
}
Person.prototype.age = 23
const person = new Person('Sunshine_lin')
for (const key in person) { console.log(key) } // name age
// 使用 hasOwnProperty
for (const key in person) {person.hasOwnProperty(key) && console.log(key)
} // name
9、valueOf 与 toString?
valueOf
- JavaScript 中的 valueOf() 方法用于返回指定对象的原始值,若对象没有原始值,则将返回对象本身。通常由JavaScript内部调用,而不是在代码中显式调用。
toString
返回对象的字符串表示
两者的共同点与不同点:
共同点:在 JavaScript 中,toString()方法和valueOf()方法,在输出对象时会自动调用。
不同点:二者并存的情况下,在数值运算中,优先调用了valueOf,字符串运算中,优先调用了toString。
返回值类型的差别:
1. toString一定将所有内容转为字符串
- valueOf取出对象内部的值,不进行类型转换
valueOf
比较偏向于计算,toString
偏向于显示- 对象转换时,优先调用
toString
- 强转字符串时优先调用
toString
,强转数字时优先调用valueOf
- 正常情况下,优先调用
toString
- 运算操作符情况下优先调用
valueOf
10、JavaScript变量在内存中具体存储形式?
基本数据类型
:存在栈内存里引用数据类型
:指针存栈内存,指向堆内存中一块地址,内容存在堆内存中
11、讲一讲JavaScript的装箱和拆箱?
装箱:把基本数据类型转化为对应的引用数据类型的操作,这就是为什么基本数据类型也有方法
const s1 = 'Sunshine_Lin'
const index = s1.indexOf('_')
console.log(index) // 8
原来是JavaScript内部进行了装箱操作
- 1、创建String类型的一个实例;
- 2、在实例上调用指定的方法;
- 3、销毁这个实例;
拆箱:将引用数据类型转化为对应的基本数据类型的操作
通过valueOf或者toString方法实现拆箱操作
var objNum = new Number(123);
var objStr =new String("123");
console.log( typeof objNum ); //object
console.log( typeof objStr ); //object
console.log( typeof objNum.valueOf() ); //number
console.log( typeof objStr.valueOf() ); //stringconsole.log( typeof objNum.toString() ); // string
console.log( typeof objStr.toString() ); // string
12、null和undefined的异同点有哪些?
相同点
- 1、都是空类型
- 2、转布尔值都是false,都是假值
- 3、null == undefined 为 true
不同点
- 1、typeof,前者为object,后者为undefined
- 2、null转数字为0,undefined转数字为NaN
- 3、null === undefined 为 false
13、如何判断数据类型?
先利用typeof判断数据的类型,如果不是object那么就直接return,如果为object就继续使用Object.prototype.toString.call()判断数据类型
14、为什么typeof null 是object?
不同数据类型底层都是用二进制来表示的,二进制前三位为000
则会被判断为object,而null二进制全是0,所以被判断成object
15、== 与 === 的区别?
==
:比较过程会进行隐式转换===
:值相同,类型相同才会为true
16、JavaScript的隐式转换规则?
什么时候发生转换
转string类型
:+(字符串连接符)转number类型
:++/–(自增自减运算符) + - * / %(算术运算符) > < >= <= == != === !== (关系运算符)转boolean
:!(逻辑非运算符) if while
// 对象:通常转换成NaN(除了只包含单个数值的数组)
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
Number(['5']) // 5
Number([]) // 0
Number(undefined) // NaN
Number(null) // 0
Number('') // 0
Number('324abc') // NaN
Number转换的时候是很严格的,只要有一个字符无法转成数值,整个字符串就会被转为NaN
parseInt()parseInt
相比Number
,就没那么严格了,parseInt
函数逐个解析字符,遇到不能转换的字符就停下来
parseInt('32a3') //32
Boolean
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
17、双等号左右两边的转换规则?
- 1、null == undefined 为 true
如果两边类型都一样就不进行类型转换,如果两边类型一样就不转换
- 2、如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
- 3、如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
- 4、如果一个操作数是对象,另一个操作数不是,则调用对象的toString()方法,用得到的基本类型值按照前面的规则进行比较
18、undefined >= undefined 为什么是 false ?
隐式转换,变成NaN >= NaN
,NaN
不等于自身也不大于自身
19.null >= null 为什么是 true?
隐式转换,变成0 >= 0
,为true
20、[] == ![] 为什么是 true ?
- 第一步:转为
[] == false
- 第二步:转为
[] == 0
- 第三步:转为
0 == 0
21、0.1 + 0.2 === 0.3,对吗?
不对,JavaScript存在精度丢失
问题,由于有些小数无法用二进制表示,所以只能取近似值,解决方法有:
- 先转大数,再变小数
- 使用
toFixed
22、什么是匿名函数?
可以使用在回调函数的地方
匿名函数,就是没有名字的函数,比如:
(function(x, y){
alert(x + y);
})(2, 3)
23、绑定点击事件有几种方式?
三种
xxx.onclick = function (){}
<xxx onclick=""></xxx>
xxx.addEventListener('click', function(){}, false)
24、addEventListener的第三个参数是干嘛的?
决定事件是捕获阶段
执行还是冒泡阶段
执行
true
:捕获false
:默认,冒泡
25、函数声明和函数表达式的区别?
函数声明
:享受函数提升函数表达式
:归类于变量声明,享受变量提升函数提升优先级 > 变量提升优先级
console.log(fun) // fun () {}
// 函数表达式
var fun = function(name) {}
// 函数声明
function fun () {}
console.log(fun) // fun (name) {}
26、JavaScript的事件流模型有哪些?
事件冒泡
:由最具体的元素接收,并往上传播事件捕获
:由最不具体的元素接收,并往下传播DOM事件流
:事件捕获 -> 目标阶段 -> 事件冒泡
27、Ajax、Axios、Fetch有啥区别?
Ajax
:是对XMLHttpRequest(XHR)的封装Axios
:是基于Promise对XHR对象的封装Fetch
:是window的一个方法,基于Promise,与XHR无关,不兼容IE
28、load、$(document).ready、DOMContentLoaded的区别?
$(document).ready、DOMContentLoaded
:DOM树构建完毕,但还没有请求静态资源load
:静态资源请求完毕
29、如何阻止事件冒泡?
function stopBubble(e) {if (e.stopPropagation) {e.stopPropagation()} else {window.event.cancelBubble = true;}
}
vue
<div @click.stop="handleClick">阻止事件冒泡</div>
30、如何阻止事件默认行为?
function stopDefault(e) {if (e.preventDefault) {e.preventDefault();} else {window.event.returnValue = false;}
}
vue
.prevent(阻止默认事件)、.capture(使用捕获模式而不是冒泡模式)等
31、什么是事件委托?
比如ul下面的li可以使用事件委托给ul绑定点击事件
当子元素都需要绑定相同事件时,可以将事件绑在父元素上,优点有:
- 绑定在父元素,则只需绑定一次,节省性能
- 后续新增或删除的子元素也可以触发父元素绑定的事件
32、如何实现数组去重?
// 使用 Map 去重
function quchong1(arr) {const newArr = []arr.reduce((pre, next) => {if (!pre.get(next)) {pre.set(next, 1)newArr.push(next)}return pre}, new Map())return newArr
}// 使用 Set 去重
function quchong (arr) {return [...new Set(arr)]
}
33、Set与Array的区别是什么?
- Set使用has判断有无元素,数组使用索引
- Set添加元素使用方法add,数组用push、unshift
- Set长度为size,数组为length
- Set会自动把同样的基础数据类型去重,数组不能
- Set删除元素用delete,数组用splice、pop、shift
- Set可以使用clear清空,数组需要重新赋值[]
- 数组可以传入new Set(array),实现数组转Set
- Set可以使用keys、value方法,转数组
- Set自带forEach方法进行遍历
34、Map与Object的区别是什么?
- Map使用set设置属性,对象使用obj[key] = value
- Map使用get获取属性值,对象使用obj[key]
- Map使用has判断属性存在与否,对象只能obj[key]
- Map删除元素使用delete方法,对象使用delete关键字
- Map使用clear进行情空,对象需要重新赋值{}
- Map和对象都可以使用entries方法转数组键值对
- Map自带forEach方法进行遍历
35、NaN是什么?有什么特点?
- typeof NaN 为 number
- NaN不等于自身,不大于自身,不小于自身
- NaN可以使用
Number.isNaN
判断 - NaN是假值,转布尔值为false
36、处理异步的方法有哪些?
- 回调函数
- Promise
- async/await
- 事件监听
- 发布订阅
37、JavaScript继承方式有几种?
前置工作
// 定义一个动物类
function Animal (name) {// 属性this.name = name || 'Animal';// 实例方法this.sleep = function(){console.log(this.name + '正在睡觉!');}
}
// 原型方法
Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};
原型链继承
核心:将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';var cat = new Cat();
console.log(cat.name); // cat
cat.eat('fish') // cat正在吃:fish
cat.sleep() // cat正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
优点:
- 1、非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 2、父类新增原型方法/属性,子类都能访问到
- 3、简单,易于实现
缺点:
- 1、要想为子类新增属性和方法,必须要在
new Animal()
这样的语句之后执行,不能放构造器中 - 2、来自原型对象的所有属性被所有实例共享
- 3、创建子实例时,无法向父类构造函数传参
- 4、不支持多继承
构造函数继承
核心:使用父类的构造器来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name) {Animal.call(this);this.name = name || 'Tom';
}var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
优点:
- 1、解决了
原型链继承
中,子类实例共享父类引用属性的问题 - 2、创建子类实例时,可以向父类传递参数
- 3、可以实现多继承(call多个父类对象)
缺点:
- 1、实例并不是父类的实例,只是子类的实例
- 2、是能继承父类的实例属性和方法,不能继承原型属性/方法
- 3、无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承
核心:通过父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){Animal.call(this);this.name = name || 'Tom';
}
Cat.prototype = new Animal();Cat.prototype.constructor = Cat;var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
优点:
- 1、弥补了
构造继承
的缺陷,可以继承实例属性/方法,也可继承原型属性/方法 - 2、既是子类的实例,也是父类的实例
- 3、不存在引用属性共享问题
- 4、可传参
- 5、函数可复用
缺点:
- 1、调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造时,就不会初始化两次实例方法/属性,避免继承组合
的缺点
function Cat(name) {Animal.call(this);this.name = name || 'Tom';
}
// 创建一个没有实例方法的类
var Super = function () { };
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();// Test Code
var cat = new Cat();
console.log(cat.name); // Tom
cat.sleep() // Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
优点:
- 1、堪称完美 缺点:
- 1、实现复杂
38、创建一个对象的方式有哪几种?
1.new Object
const obj = new Object()
obj.name = 'Sunshine_Lin'
2.字面量
const obj = { name: 'Sunshin_Lin' }
3.工厂模式
function createObj(name) {const obj = new Object()obj.name = namereturn obj
}
const obj = createObj('Sunshine_Lin')
构造函数
function Person(name) {this.name = name
}
const person = new Person('Sunshine_Lin')
39、this指向的四种情况?
this
是当前执行上下文(global
、function
或 eval
)的一个属性。
也就是说,我们可以把 JS 中的 this
分为两种,分别是:
- 全局上下文中的
this
。 - 函数上下文中的
this
。
全局上下文的this
全局上下文的 this
指向非常明确,不管有没有启用严格模式,都指向 window
对象。
<body><script>console.log(this === window); // 输出 true("use strict");console.log(this === window); // 输出 true</script>
</body>
给 this
添加属性,就相当于给 window
添加属性,给 window
添加属性,就相当于给 this
添加属性,如下代码所示:
this.userName = "zhangsan";
window.age = 18;console.log(this.age); // 输出 18
console.log(window.userName); // 输出 'zhangsan'
函数上下文的 this
全局上下文的 this 很简单,记住无脑指向 window 就完事,复杂就复杂在函数上下文的 this,函数上下文中的 this 与 arguments 一样,就是函数的隐式参数,可以在任意函数中调用,它的指向不是固定不变的,取决于函数处于什么位置、以什么方式调用,可以总结成如下图:
全局上下文中的函数
直接调用全局上下文中的函数,this
指向默认情况下为 window
。 但是在严格模式下,this
指向的就是 undefined
。
function fn() {console.log(this); // 输出 window
}
fn();function fn() {"use strict";console.log(this); // 输出 undefined
}
fn();
对象中的函数
调用对象中的函数,this
指向为这个对象。
const obj = {a: 1,fn() {console.log("this :>> ", this); // 输出 objconsole.log("this.a :>> ", this.a); // 输出 1},
};obj.fn();
但是如果函数嵌套有函数,此时的 this
指向为 window
,就非常令人迷惑。
const obj = {a: 1,fn() {return function () {console.log("this :>> ", this); // 输出 windowconsole.log("this.a :>> ", this.a); // 输出 100};},
};var a = 100;obj.fn()();
其实可以这么理解:
obj.fn()();等价于;const temp = obj.fn(); // 定义一个临时变量来存储 obj.fn 返回的函数
temp(); // 执行这个函数
上面代码示例中的 temp
在运行时是处在 window
环境中的,所以 this
指向 window
。
对于箭头函数,它不会创建自己的 this
,它只会从自己的作用域链的上一层继承 this。
构造函数内,如果返回值是一个对象,this
指向这个对象,否则 this
指向新建的实例。
function Person(name) {this.name = name;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
function Person(name) {this.name = name;return {name: "xxx",};
}
const p = new Person("zhangsan");
console.log(p.name); // 'xxx'
function Person(name) {this.name = name;return {};
}
const p = new Person("zhangsan");
console.log(p.name); // 'undefined'
如果有返回值,但是不是一个对象,this
还是指向实例。
function Person(name) {this.name = name;return 123;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
更改this指向
call
使用call()方法调用函数,同时更改被调用函数的this指向
function fn() {console.log(this.name);
}const obj = {name: "zhangsan",
};
fn.call(obj); // 指定 this 为 obj,输出 'zhangsan'
apply
使用apply()方法调用函数,同时更改被调用函数的this指向
apply
和 call
的功能完全一样,只是传参形式不一样,call
是传多个参数,apply
是只传参数集合。
function add(x, y, z) {return this.x + this.y + this.z;
}const obj = {x: 1,y: 2,z: 3,
};console.log(add.call(obj, 1, 2, 3)); // 输出 6
console.log(add.apply(obj, [1, 2, 3])); // 输出 6,只是传参形式不同而已
bind
使用bind()不会调用函数,可以更改this指向
bind
和 call
、apply
的区别是,函数调用 call
和 apply
会直接调用,而调用 bind
是创建一个新的函数,必须手动去再调用一次,才会生效
function add(x, y, z) {return this.x + this.y + this.z;
}const obj = {x: 1,y: 2,z: 3,
};const add1 = add.bind(obj, 1, 2, 3); // bind 会返回一个新的函数
console.log(add1()); // 执行新的函数,输出 6
区别
call和apply会调用函数,并且更改内部this指向
call和apply传递的参数不一样,call传递的参数arg1,arg2… apply传递参数[arg1,arg2…]
bind不会调用函数
40、数组的常用方法有哪些?
方法 | 作用 | 是否影响元素原数组 |
---|---|---|
push | 在数组后添加元素,返回长度 | ✅ |
pop | 删除数组最后一项,返回被删项 | ✅ |
shift | 删除数组第一项,返回被删项 | ✅ |
unshift | 数组开头添加元素,返回长度 | ✅ |
reserve | 反转数组,返回数组 | ✅ |
sort | 排序数组,返回数组 | ✅ |
splice | 截取数组,返回被截取部分 | ✅ |
join | 将数组变字符串,返回字符串 | ❌ |
concat | 连接数组 | ❌ |
map | 相同规则处理数组项,返回新数组 | ❌ |
forEach | 遍历数组 | ❌ |
filter | 过滤数组项,返回符合条件的数组 | ❌ |
every | 每一项符合规则才返回true | ❌ |
some | 只要有一项符合规则就返回true | ❌ |
reduce | 接受上一个return和数组下一项 | ❌ |
flat | 数组扁平化 | ❌ |
slice | 截取数组,返回被截取区间 | ❌ |
41、Math的常用方法有哪些?
方法 | 作用 |
---|---|
Math.max(…arr) | 取arr中的最大值 |
Math.min(…arr) | 取arr中的最小值 |
Math.ceil(小数) | 小数向上取整 |
Math.floor(小数) | 小数向下取整 |
Math.round(小数) | 小数四舍五入 |
Math.sqrt(num) | 对num进行开方 |
Math.pow(num, m) | 对num取m次幂 |
Math.random() * num | 取0-num的随机数 |
42、哪些因素导致内存泄漏?如何解决?
43、讲讲JavaScript的垃圾回收机制
https://mp.weixin.qq.com/s?__biz=Mzg2NjY2NTcyNg==&mid=2247483683&idx=1&sn=973b0280aa9c6eb1678ea530ee4d2381&chksm=ce4614b2f9319da402278b86e63f44e818e832ecbaafdada344ba979de3df4443a8ed53b0cb1&scene=21#wechat_redirect
44、JS中有哪些不同类型的弹出框?
在JS中有三种类型的弹出框可用,分别是:
- Alert
- Confirm
- Prompt
45. 如何将 JS 日期转换为ISO标准
var date = new Date();
var n = date.toISOString();
console.log(n);
// YYYY-MM-DDTHH:mm:ss.sssZ
46、如何在JS中编码和解码 URL
- 编码:encodeURI()
- 解码:decodeURI()
47、什么是BOM?有哪些api?
BOM就是browser object model
,浏览器对象模型
api | 作用 | 代表方法或属性 |
---|---|---|
window.history | 操纵浏览器的记录 | history.back() history.go(-1) |
window.innerHeight | 获取浏览器窗口的高度 | |
window.innerWidth | 获取浏览器窗口的宽度 | |
window.location | 操作刷新按钮和地址栏 |
48、BOM 和 DOM 的关系
BOM全称Browser Object Model,即浏览器对象模型,主要处理浏览器窗口和框架。
DOM全称Document Object Model,即文档对象模型,是 HTML 和XML 的应用程序接口(API),遵循W3C 的标准,所有浏览器公共遵守的标准。
JS是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM的根节点。
可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。
49、JS中的substr()和substring()函数有什么区别
substr() 函数的形式为substr(startIndex,length)。它从startIndex返回子字符串并返回’length’个字符数。
var s = "hello";
( s.substr(1,4) == "ello" ) //
substring() 函数的形式为substring(startIndex,endIndex)。它返回从startIndex到endIndex - 1的子字符串。
var s = "hello";
( s.substring(1,4) == "ell" ) // true
50、解释一下 “use strict” ?
“use strict”是Es5中引入的js指令。使用“use strict”指令的目的是强制执行严格模式下的代码。在严格模式下,咱们不能在不声明变量的情况下使用变量。早期版本的js忽略了“use strict”。
禁止this关键字指向全局对象
'use strict'function fn() {console.log(this) undefinedthis.name = 'jack' 报错}fn()
禁止意外创建全局变量
/ 1. 禁止意外创建全局变量,
message = "Hello World"
// 在严格模式下这种未定义就赋值的写法是不被允许的
console.log(message)
function foo () {age = 20// 在严格模式下这种未定义就赋值的写法是不被允许的,而在非严格模式下会创建一个age全局变量
}