前端面试题 -- JavaScript (一)

前言

前两天总结了一下HTML+CSS方面的面试题 (传送门),今天翻看了一些 JavaScript 面试中常见的几个问题(只是一部分,会持续更新),分享给有须要的小伙伴,欢迎star关注html

若是文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过git

如下 ↓github

1. JavaScript 有哪些数据类型

6种原始数据类型:web

  • Boolean: 布尔表示一个逻辑实体,能够有两个值:truefalse
  • Number: 用于表示数字类型
  • String: 用于表示文本数据
  • Null: Null 类型只有一个值: null,特指对象的值未设置
  • Undefined: 一个没有被赋值的变量会有个默认值 undefined
  • Symbol: 符号(Symbols)是ECMAScript第6版新定义的。符号类型是惟一的而且是不可修改的

引用类型:Object面试

详见 JavaScript中的数据类型segmentfault

2. 怎么判断不一样的JS数据类型

  • typeof 操做符:返回一个字符串,表示未经计算的操做数的类型
typeof 操做符对于简单数据类型,返回其自己的数据类型,函数对象返回 function ,其余对象均返回 Object

null 返回 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 属性,并让其指向该函数的引用
nullundefined是无效的对象,所以是不会有 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表示"没有对象",即该处不该该有值

典型用法:app

  1. 做为函数的参数,表示该函数的参数不是对象
  2. 做为对象原型链的终点
undefined表示"缺乏值",就是此处应该有一个值,可是尚未定义

典型用法:

  1. 变量被声明了,但没有赋值时,就等于undefined
  2. 调用函数时,应该提供的参数没有提供,该参数等于undefined
  3. 对象没有赋值的属性,该属性的值为undefined
  4. 函数没有返回值时,默认返回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数据标准类型的对象

详见 JavaScript中的浅拷贝与深拷贝

7. 什么是闭包,为何要用它

简单来讲,闭包就是可以读取其余函数内部变量的函数
function Person() {
    var name = 'hello'
    function say () {
        console.log(name)
    }
    return say()
}
Person() // hello
因为 JavaScript 特殊的做用域,函数外部没法直接读取内部的变量,内部能够直接读取外部的变量,从而就产生了闭包的概念

用途:

最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中

注意点:

因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露

详见 JavaScript 中的闭包

8. 介绍一下 JavaScript 原型,原型链,它们有何特色

首先明确一点,JavaScript是基于原型的

每一个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.

image

图解:

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

那么什么是原型链:

JavaScript中全部的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有本身的原型对象,这样层层上溯,就造成了一个相似链表的结构,这就是原型链

全部原型链的终点都是Object函数的prototype属性。Objec.prototype指向的原型对象一样拥有原型,不过它的原型是null,而null则没有原型

image

详见 JavaScript中的原型与原型链

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
最简单的继承实现方式,可是也有其缺点
  1. 来自原型对象的全部属性被全部实例共享
  2. 建立子类实例时,没法向父类构造函数传参
  3. 要想为子类新增属性和方法,必需要在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)

固然,还有不少种实现继承的方式,这里就很少说了。而后,再推荐一波 红宝书

详见 JavaScript中的继承

10. new 操做符具体干了什么

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

后记

这里如今只是JavaScript 面试题中的一部分,后面是持续更新, 有须要的小伙伴能够关注哦好了,周末愉快 [啊!结束了……]

相关文章
相关标签/搜索