前端面试题 — JavaScript (一)

5次阅读

共计 5606 个字符,预计需要花费 15 分钟才能阅读完成。

前言
前两天总结了一下 HTML+CSS 方面的面试题 (传送门),今天翻看了一些 JavaScript 面试中常见的几个问题(只是一部分,会持续更新),分享给有需要的小伙伴
如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过
以下 ↓
1. JavaScript 有哪些数据类型
6 种原始数据类型:

Boolean: 布尔表示一个逻辑实体,可以有两个值:true 和 false

Number: 用于表示数字类型
String: 用于表示文本数据
Null: Null 类型只有一个值:null, 特指对象的值未设置
Undefined: 一个没有被赋值的变量会有个默认值 undefined

Symbol: 符号 (Symbols) 是 ECMAScript 第 6 版新定义的。符号类型是唯一的并且是不可修改的

引用类型:Object
详见 JavaScript 的数据类型 -MDN
2. 怎么判断不同的 JS 数据类型

typeof 操作符:返回一个字符串,表示未经计算的操作数的类型

typeof 操作符对于简单数据类型,返回其本身的数据类型,函数对象返回 function,其他对象均返回 Objectnull 返回 Object

instanceof: 用来判断 A 是否是 B 的实例,表达式为 A instanceof B,返回一个 Boolean 类型的值

instanceof 检测的是原型, 只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型

let a = [];
a instanceof Array // true
a instanceof Object // true
变量 a 的 __proto__ 直接指向 Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,a 就是 Object 的实例. 针对数组的这个问题,ES5 提供了 Array.isArray() 方法。该方法用以确认某个对象本身是否为 Array 类型
constructor: 当一个函数被定义时,JS 引擎会为其添加 prototype 原型,然后再在 prototype 上添加一个 constructor 属性,并让其指向该函数的引用

null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

function F() {};
var f = new F;
f.constructor == F // true

F.prototype = {a: 1}
var f = new F
f.constructor == F // false
在构造函数 F.prototype 没有被重写之前,构造函数 F 就是新创建的对象 f 的数据类型。当 F.prototype 被重写之后,原有的 constructor 引用丢失, 默认为 Object 因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改

toString: Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]]。这是一个内部属性,其格式为 [object Xxx],其中 Xxx 就是对象的类型
Object.prototype.toString.call(”) ; // [object String]
Object.prototype.toString.call(11) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call([]) ; // [object Array]
3. undefined 和 null 有什么区别

null 表示 ” 没有对象 ”,即该处不应该有值
典型用法:

作为函数的参数,表示该函数的参数不是对象
作为对象原型链的终点

undefined 表示 ” 缺少值 ”,就是此处应该有一个值,但是还没有定义
典型用法:

变量被声明了,但没有赋值时,就等于 undefined

调用函数时,应该提供的参数没有提供,该参数等于 undefined

对象没有赋值的属性,该属性的值为 undefined

函数没有返回值时,默认返回 undefined

详见:undefined 和 null 的区别 - 阮一峰
4. 数组对象有哪些常用方法
修改器方法:

pop(): 删除数组的最后一个元素,并返回这个元素
push():在数组的末尾增加一个或多个元素,并返回数组的新长度
reverse(): 颠倒数组中元素的排列顺序
shift(): 删除数组的第一个元素,并返回这个元素
unshift(): 在数组的开头增加一个或多个元素,并返回数组的新长度
sort(): 对数组元素进行排序,并返回当前数组
splice(): 在任意的位置给数组添加或删除任意个元素

访问方法:

concat(): 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组
join(): 连接所有数组元素组成一个字符串
slice(): 抽取当前数组中的一段元素组合成一个新数组
indeOf(): 返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1
lastIndexOf(): 返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1

迭代方法:

forEach(): 为数组中的每个元素执行一次回调函数, 最终返回 undefined

every(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false
some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false
filter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回
map(): 返回一个由回调函数的返回值组成的新数组

更多方法请参考 MDN 传送门
5. Js 有哪几种创建对象的方式
对象字面量
var obj = {}
Object 构造函数
var obj = new Object()
工厂模式
function Person(name, age) {
var o = new Object()
o.name = name;
o.age = age;
o.say = function() {
console.log(name)
}
return o
}
缺点:每次通过 Person 创建对象的时候,所有的 say 方法都是一样的,但是却存储了多次,浪费资源
构造函数模式
function Person(name, age) {
this.name = name
this.age = age
this.say = function() {
console.log(name)
}
}
var person = new Person(‘hello’, 18)
构造函数模式隐试的在最后返回 return this 所以在缺少 new 的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给 window 对象, 可以根据 return this 的特性调用 call 或者 apply 指定 this
原型模式
function Person() {}
Person.prototype.name = ‘hanmeimei’;
Person.prototype.say = function() {
alert(this.name);
}
Person.prototype.friends = [‘lilei’];
var person = new Person();
实现了方法与属性的共享,可以动态添加对象的属性和方法。但是没有办法创建实例自己的属性和方法,也没有办法传递参数
构造函数和原型组合
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.say = function() {
console.log(this.name)
}
var person = new Person(‘hello’)
还有好几种模式,感兴趣的小伙伴可以参考 红宝书,你们肯定知道的了!
6. 怎么实现对对象的拷贝(浅拷贝与深拷贝)
浅拷贝

拷贝原对象引用
可以使用 Array.prototype.slice()也可以完成对一个数组或者对象的浅拷贝

Object.assign()方法

深拷贝
最常用的方式就是 JSON.parse(JSON.stringify(目标对象),缺点就是只能拷贝符合 JSON 数据标准类型的对象
7. 什么是闭包,为什么要用它
简单来说,闭包就是能够读取其他函数内部变量的函数
function Person() {
var name = ‘hello’
function say () {
console.log(name)
}
return say()
}
Person() // hello
由于 JavaScript 特殊的作用域,函数外部无法直接读取内部的变量,内部可以直接读取外部的变量,从而就产生了闭包的概念
用途:
最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中
注意点:
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露
8. 介绍一下 JavaScript 原型,原型链,它们有何特点
首先明确一点,JavaScript 是基于原型的
每个构造函数 (constructor) 都有一个原型对象 (prototype), 原型对象都包含一个指向构造函数的指针, 而实例(instance) 都包含一个指向原型对象的内部指针.

图解:

每一个构造函数都拥有一个 prototype 属性,这个属性指向一个对象,也就是原型对象
原型对象默认拥有一个 constructor 属性,指向指向它的那个构造函数
每个对象都拥有一个隐藏的属性[[prototype]],指向它的原型对象

那么什么是原型链:

JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链所有原型链的终点都是 Object 函数的 prototype 属性。Objec.prototype 指向的原型对象同样拥有原型,不过它的原型是 null,而 null 则没有原型

9. JavaScript 如何实现继承
原型链继承
function Animal() {}
Animal.prototype.name = ‘cat’
Animal.prototype.age = 1
Animal.prototype.say = function() {console.log(‘hello’)}

var cat = new Animal()

cat.name // cat
cat.age // 1
cat.say() // hello
最简单的继承实现方式,但是也有其缺点

来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
要想为子类新增属性和方法,必须要在 new 语句之后执行,不能放到构造器中

构造继承
function Animal() {
this.species = “ 动物 ”
}
function Cat(name, age) {
Animal.call(this)
this.name = name
this.age = age
}

var cat = new Cat(‘ 豆豆 ’, 2)

cat.name // 豆豆
cat.age // 2
cat.species // 动物
使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上.
组合继承
function Animal() {
this.species = “ 动物 ”
}

function Cat(name){
Animal.call(this)
this.name = name
}

Cat.prototype = new Animal() // 重写原型
Cat.prototype.constructor = Cat

如果没有 Cat.prototype = new Animal()这一行,Cat.prototype.constructor 是指向 Cat 的;加了这一行以后,Cat.prototype.constructor 指向 Animal. 这显然会导致继承链的紊乱(cat1 明明是用构造函数 Cat 生成的),因此我们必须手动纠正,将 Cat.prototype 对象的 constructor 值改为 Cat

extends 继承
ES6 新增继承方式,Class 可以通过 extends 关键字实现继承
class Animal {

}

class Cat extends Animal {
constructor() {
super();
}
}
使用 extends 实现继承,必须添加 super 关键字定义子类的 constructor,这里的 super() 就相当于 Animal.prototype.constructor.call(this)

当然,还有很多种实现继承的方式,这里就不多说了。然后,再推荐一波 红宝书
10. new 操作符具体干了什么

创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型
属性和方法被加入到 this 引用的对象中
新创建的对象由 this 所引用,并且最后隐式的返回 this

后记
这里现在只是 JavaScript 面试题中的一部分,后面是持续更新,有需要的小伙伴可以关注哦好了,周末愉快 [啊!结束了……]

正文完
 0