前端JS基础面试技巧
讲解 JS 基础语法相关的面试题,分析原理以及解答方法。这一章节讲解了基础知识的第一部分:变量的类型和计算。以及JS “三座大山” —— 原型、作用域和异步中的: 原型和原型链、作用域和闭包。
知识点:
2-1 变量类型和计算
2-2 原型和原型链
2-3 函数声明和函数表达式
2-4 作用域和闭包
关于面试
- 基层工程师 - 基础知识
- 高级工程师 - 项目经验
- 架构师 - 解决方案
先从几道面试题入手
- JS 中使用 typeof 能得到的哪些类型?
- 考点:JS 变量类型
- 何时使用 === 何时使用 **== **?
- 考点:强制类型转换
- window.onload 和 DOMContentLoaded 的区别?
- 考点:浏览器的渲染过程
- 用 JS 创建10个 a 标签,点击的时候弹出来对应的序号
- 考点:作用域
- 简述如何实现一个模块加载器,实现类似 require.js 的基本功能
- 考点:JS 模块化
- 实现数组的 随机排序
- 考点:JS 基础算法
知识体系
题目
知识点
解答
2-1 变量类型和计算
2-1 变量类型和计算
题目
知识点
解答
题目
- JS 中使用 typeof 能得到的哪些类型?
- 何时使用 === 何时使用 **== **?
- JS 中有哪些 内置函数
- JS 变量按照 存储方式 分为哪些类型,并描述其特点
- 如何理解 JSON
知识点
- 变量类型
- 值类型 vs 引用类型
- typeof 运算符 详解
- 变量计算
变量类型
值类型 vs 引用类型
值类型
值类型 (基本数据类型) 的值是按值访问的。
基本类型的值是不可变的,基本类型的比较是它们的值的比较,基本类型的变量是存放在 栈内存(Stack)里的。
JavaScript 数据类型 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol (ES6提供的新的类型)。
6种基本数据类型:string、number、boolean、undefined、null 、Symbol 。
引用类型
引用类型的值是按引用访问的。
引用类型的值是可变的,引用类型的比较是引用的比较,引用类型的值是保存在 堆内存(Heap)中的对象(Object)。
特点:无限制扩展属性
3种 主要引用类型:对象(Object)、数组(Array)、函数(Function)
细分的话,有:
Object 类型
、Array 类型
、Date 类型
、RegExp 类型
、Function 类型
等。
数据类型
**值类型(基本类型) **+ 引用数据类型
7种数据类型:number、string、boolean、undefined、null、Symbol 、Object (Object、Array、Function) 。
typeof 运算符
7 种类型:undefined、string、number、boolean、object、function 、symbol(ES6提供的新的类型)
注意:typeof null // object
typeof 运算符 只能 区分 值类型 的 类型,对于引用类型的 对象、数组 区分不出来 。
变量计算
这个主要针对值类型- 强制类型转换
4种强制类型转换:
- 字符串拼接
- == 运算符
- if 语句
- 逻辑运算
字符串拼接
== 运算符
if 语句
if 语句
false 情况:0、NaN、’<空字符串>’、null、undefine 、false
逻辑运算符
何时使用 === 和 ==
何时使用 === 和 ==?
解答:参考jQuery源码中推荐的写法,除了判断对象属性是否为空 和 **看是否函数的参数为空 ** 的情况 ,其余的都用 === 。
==
: 只进行值的比较
===
: 不仅进行值得比较,还要进行数据类型的比较
JS中的内置函数
JS中的内置函数的作用
JS按存储方式区分变量类型
基本类型的值是不可变的
如何理解 JSON
JS 内置对象,Math也是内置对象
注意:JSON 既是一个JS 内置对象,也是一种 数据格式。
2-2 原型和原型链
2-2 原型和原型链
题目
知识点
解答
题目
- 如何准确判断一个变量是 数组类型
- 写一个原型链继承的例子
- 描述 new 一个对象的过程
- zepto (或其他框架) 源码中如何使用原型链
知识点
- 构造函数
- 构造函数-扩展
- 原型规则和示例
- 原型链
- instanceof
构造函数
函数名 习惯 第一个字母大写( 高级程序员规范)
构造函数扩展
构造函数扩展
函数扩展 ---- 语法糖
5条原型规则和示例
5条原型规则
原型规则 是学习 原型链 的基础
5条原型规则 :
-
所有的 引用类型 (对象,数组,函数),都具有对象特性,即可 自由扩展 属性(除了null以外)。
-
所有的 引用类型 (对象,数组,函数),都有一个
__proto__
( 隐式原型 )属性,属性值都是一个普通对象。 -
所有的函数都有一个
prototype
( 显示原型 )属性,属性值是一个普通对象。 -
所有的引用类型(对象,数组,函数),
__proto__
属性值指向它的构造函数的prototype
属性值。 -
当试图得到一个引用类型的某个属性时,如果这个对象本身没有这个属性,那么会去它的
__proto__
(即它的构造函数的prototype
)中去找。
示例 :
循环自身的属性:
原型链
这种搜索的轨迹,形似一条长链, 又因 prototype 在这个游戏规则中充当链接的作用,于是我们把这种实例与原型的链条称作 原型链
instanceof
用于 判断 引用类型 属于哪个 构造函数的方法
**instanceof 运算符 ** 用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);// true
console.log(auto instanceof Object);// true
解答
-
如何准确判断一个变量是 数组类型
- arr instanceof Arrar
-
写一个原型链继承的例子、
function Elem(id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function (val) {
var elem = this.elem
if (val) {
elem.innerHTML = val
return this //链式操作
} else {
return elem.innerText
}
}
Elem.prototype.on = function (type, fn) {
var elem = this.elem
elem.addEventListener(type, fn)
return this
}
var div1 = new Elem('div1')
div1.html('<p>hello world</p>').on('click', function () {
alert('clicked')
})
-
描述 new 一个对象的过程
-
zepto (或其他框架) 源码中如何使用原型链
2-3 函数声明和函数表达式
函数声明和函数表达式
函数声明
fn() //执行
function fn(){
//声明
}
函数表达式
把var定义的变量提前:相当于:先定义 var fn — > 然后执行 fn()
fn() // TypeError: fn is not a function
var fn=function(){
// 表达式
}
相关的例子(函数执行的顺序):
console.log(a) // undefined
var a=100
fn('zhouchen')
function fn(name) {
age=20
console.log(name,age)
var age
}
//output:zhouchen 20
fn('zhouchen')
function fn(name) {
console.log(arguments) // 参数的集合
age = 20
console.log(name, age)
var age
bar(100)
function bar(num) {
console.log(num)
}
}
/*
{ '0': 'zhouchen' }
zhouchen 20
100
*/
2-4 作用域和闭包
作用域和闭包
题目
知识点
解答
题目
- 说一下对变量提升的理解
- 说明 this 几种 不同的使用场景
- 创建10个 a 标签,点击的时候弹出来对应的序号
- 如何理解作用域
- 实际开发中闭包的应用
知识点
-
执行上下文
-
this
-
作用域
-
作用域链
-
闭包
执行上下文
执行上下文
this
this要在 执行时 才能确定值,定义时 无法确认
块级作用域
任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
JS 并 不支持 块级作用域,它只支持 函数作用域 ,而且在一个函数中的任何位置定义的变量 在该函数中的 任何地方都是可见的 。
if(true){
var name='zhouchen'
}
console.log(name) // zhouchen
链式作用域
如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的 "链式作用域" 结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
闭包
上一节代码中的 f2函数,就是闭包。
简单来说:闭包就是 能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 定义在一个函数内部的函数 。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
实际开发中闭包的应用
实际开发中闭包的应用