在去年底开始换工作,直到现在算是告了一个段落,断断续续的也面试了不少公司,现在回想起来,那段时间经历了被面试官手撕,被笔试题狂怼,悲伤的时候差点留下没技术的泪水。
这篇文章我打算把我找工作遇到的各种面试题(每次面试完我都会总结)和我自己复习遇到比较有意思的题目,做一份汇总,年后是跳槽高峰期,也许能帮到一些小伙伴。
先说下这些题目难度,大部分都是基础题,因为这段经历给我的感觉就是,不管你面试的是高级还是初级,基础的知识一定会问到,甚至会有一定的深度,所以基础还是非常重要的。
我将根据类型分为几篇文章来写:
面试总结:javascript 面试点汇总(万字长文)(已完成) 强烈大家看看这篇,面试中 js 是大头
面试总结:nodejs 面试点汇总(已完成)
面试总结:浏览器相关 面试点汇总(已完成)
面试总结:css 面试点汇总(已完成)
面试总结:面试技巧篇(已完成)
六篇文章都已经更新完啦~
这篇文章是对 javascript
相关的题目做总结,内容有点长,大致算了下,有接近 2W 字,推荐用电脑阅读,欢迎朋友们先收藏在看。
先看看目录(这长图在手机上比较模糊,可点击图片看大图)
Q:介绍下原型链
原型链这东西,基本上是面试必问,而且不是知识点还都是基于原型链扩展的,所以我们先把原先链整明白。
我们看一张网上非常流行的图
嗯,箭头有点多且有点绕,没关系,我们可逐步分析,我们从结果倒推结论,这样更直观些,看代码
function person() {
this.name = 10
}
person.prototype.age = 10
const p = new person()
分析构造函数
我们通过断点看下 person 这个函数的内容
它是一个自定义的函数类型,看关键的两个属性 prototype
和 __proto__
,我们一一分析
- prototype 分析
对 prototype
展开看,是个自定义的对象,这个对象有三个属性 age constructor __proto__
,
age
的值是 10 ,那么可以得出通过person.prototype
赋值的参数都是在 prototype
这个对象中的。
点开 constructor
,发现这个属性的值就是指向构造器 preson
函数,其实就是循环引用,这时候就有点套娃的意思了
那么,根据字面意思, prototype
可以翻译成,原先对象,用于扩展属性和方法。
__proto__
分析
对__proto__
展开看看
person 中的 __proto__
是一个原始的 function 对象,在 function 对象中,又看到了 __proto__
这个属性,这时候它的值是原始的 Object 对象,在 Object 对象中又再次发现了 __proto__
属性,这时候 __proto__
等于 null
js 中数据类型分为两种,基本类型和对象类型,所以我们可以这么猜测,person 是一个自定义的函数类型,它应该是属于函数这一家族下的,对于函数,我们知道它是属于对象的,那么它们几个是怎么关联起来的呢?
没错,就是通过 __proto__
这个属性,而由这个属性组成的链,就叫做原型链。
根据上面的例子我们,可得出,原型链的最顶端是 null
,往下是 Object
对象,而且只要是对象或函数类型都会有 __proto__
这个属性,毕竟大家都是 js-family 的一员嘛。
分析生成的对象
上面我们已经知道了原型和原型链,那么对于 new 出来的对象,它们的关系又是怎么样的呢?继续断点分析
p
对象中有个 __proto__
属性,我们已经知道这是个原型链,通过它可以找到我们的祖先,展开 __proto__
,大家看到这里有没有发现很眼熟,在看一张图,
没错!p.__proto__
就是 person 函数的 prototype
,这一步也就是 new 的核心点(下个题目我们会说到)。
那么 p 这实例的原型链是怎么样的?
p.__proto__ => {constructor:func}.__proto__ => Object => null
对于实例对象来说,原先链主要用来做什么呢?
- 实现继承:如果没有原型链,每个对象就都是孤立的,对象间就没有关联,所以原型链就像一颗树干,从而可以实现面对对象中的继承
- 属性查找:首先在当前实例对象上查找,要是没找到,那么沿着
__proto__
往上查找 - 实例类型判断:判断这个实例是否属于某类对象
还有就是,光看文字的解释还是有点费解的,要想深入理解,还是需要多动手断点调试,才能很快理顺。
若还是不太理解实例对象的原型链关系,可以看下一题:解释构造函数
Q:介绍下构造函数是什么?
构造函数与普通函数在编码上没有区别,只要可以通过 new 来调用的就是构造函数。
那么什么函数不可以作为构造函数呢?
箭头函数不可以作为构造函数。
new
是一个语法糖,对执行的原理一步步拆分并自己写一个模拟 new 的函数:
0. 自定义一个 objectFactory 模拟 new 语法糖,函数可以接受多个参数,但要求第一个参数必须为构造函数
-
创建一个空对象 obj ,分配内存空间
-
从参数列表中获取构造函数,并将
obj
的__proto__
属性指向构造函数的prototype
-
通过
apply
执行构造,并将当前this
的指向改为obj
-
返回构造函数的执行结果,或者当前的
obj
对象
function objectFactory() {
var obj = {
},
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj;
};
function fnf() {
this.x = 123
}
let a2 = objectFactory(fnf) // 模拟 new fnf()
console.log(a2.x) // 123
可看出并不复杂,关键点在第二步,设置对象的原型链,这也是创建实例对象的核心点。
Q:typeof 和 instanceof 有什么区别
js 中数据类型分为两类,一类是基本数据类型,一类是对象类型。
基本数据类型有:Number String Boolean Null Undefined BigInt Symbol
对象类型: Object
也叫引用类型
- typeof(a) 用于返回值的类型,有 “number”、“string”、“boolean”、“null”、“function” 和 “undefined”、“symble”、“object”
let a = 1
let a1 = '1'
let a2 = true
let a3 = null
let a4 = undefined
let a5 = Symbol
let a6 = {
}
console.log(typeof(a),typeof(a1),typeof(a2),typeof(a3),typeof(a4),typeof(a5),typeof(a6))
// number string boolean object undefined function object
- instanceof 用于判断该对象是否是目标实例,根据原型链
__proto__
逐层向上查找,通过 instanceof 也可以判断一个实例是否是其父类型或者祖先类型的实例。
有这么个面试题
function person() {
this.name = 10
}
console.log(person instanceof person)
结果是 false
,看下 person 函数的原型链 person.
proto_ => Function.
proto=> Object.
proto=> null
,所以在原型链上是找不到 person
的
Q:数据类型有哪几种?
- 7 种原始数据类型:
Null
Undefined
String
Number
Boolean
BigInt
Symbol
- Object 对象类型,也称为引用类型
Q:JS中基本数据类型和引用类型在内存上有什么区别?
基本类型:存储在栈内存中,因为基本类型的大小是固定,在栈内可以快速查找。
引用类型:存储在堆内存中,因为引用类型的大小是不固定的,所以存储在堆内存中,然后栈内存中仅存储堆中的内存地址。
我们在查找对象是从栈中查找,那么可得知,对于基本对象我们是对它的值进行操作,而对于引用类型,我们是对其引用地址操作。
var name = 'xiaoming'
var name1 = name; // 值拷贝
var obj = {
age:10}
var obj1 = obj // 引用地址的拷贝,所以这两个对象指向同一个内存地址,那么他们其实是同一个对象
关于函数的传参是传值还是传引用呢?
很多人说基本类型传值,对象类型传引用,但严格来说,函数参数传递的是值,上图可以看出,就算是引用类型,它在栈中存储的还是一串内存地址,所以也是一个值。不过我觉得没必要过于纠结这句话,理解就行。
Q:描述 NaN 指的是什么
NaN 属性是代表非数字值的特殊值,该属性用于表示某个值不是数字。
NaN 是 Number 对象中的静态属性
typeof(NaN) // "number"
NaN == NaN // false
那怎么判断一个值是否是 NAN 呢? 若支持 es6
,可直接使用 Number.isNaN()
若不支,可根据 NAN !== NAN
的特性
function isReallyNaN(val) {
let x = Number(val);
return x !== x;
}
Q:描述 null
null 是基本类型之一,不是 Object 对象,至于为什么?答曰:历史原因,咱也不敢多问