zlcjc

前端JS基础面试技巧

讲解 JS 基础语法相关的面试题,分析原理以及解答方法。这一章节讲解了基础知识的第一部分:变量的类型和计算。以及JS “三座大山” —— 原型、作用域和异步中的: 原型和原型链、作用域和闭包。

知识点:

2-1 变量类型和计算

2-2 原型和原型链

2-3 函数声明和函数表达式

2-4 作用域和闭包

mark

关于面试

  • 基层工程师 - 基础知识
  • 高级工程师 - 项目经验
  • 架构师 - 解决方案

先从几道面试题入手

  • JS 中使用 typeof 能得到的哪些类型?
    • 考点:JS 变量类型
  • 何时使用 === 何时使用 *== *
    • 考点:强制类型转换
  • window.onloadDOMContentLoaded 的区别?
    • 考点:浏览器的渲染过程
  • 用 JS 创建10个 a 标签,点击的时候弹出来对应的序号
    • 考点:作用域
  • 简述如何实现一个模块加载器,实现类似 require.js 的基本功能
    • 考点:JS 模块化
  • 实现数组的 随机排序
    • 考点:JS 基础算法

mark

mark

知识体系

题目

知识点

解答

2-1 变量类型和计算

2-1 变量类型和计算

题目

知识点

解答

题目

  • JS 中使用 typeof 能得到的哪些类型?
  • 何时使用 === 何时使用 *== *
  • JS 中有哪些 内置函数
  • JS 变量按照 存储方式 分为哪些类型,并描述其特点
  • 如何理解 JSON

知识点

  • 变量类型
    • 值类型 vs 引用类型
    • typeof 运算符 详解
  • 变量计算

变量类型

值类型 vs 引用类型

值类型

值类型 (基本数据类型) 的值是按值访问的。

基本类型的值是不可变的,基本类型的比较是它们的值的比较,基本类型的变量是存放在 栈内存(Stack)里的

JavaScript 数据类型类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol (ES6提供的新的类型)。

6种基本数据类型:stringnumberbooleanundefinednullSymbol

mark

引用类型

引用类型的值是按引用访问的。

引用类型的值是可变的,引用类型的比较是引用的比较,引用类型的值是保存在 堆内存(Heap)中的对象(Object)

特点:无限制扩展属性

3种 主要引用类型:对象(Object)、数组(Array)、函数(Function

细分的话,有:Object 类型Array 类型Date 类型RegExp 类型Function 类型 等。

mark

数据类型

值类型(基本类型) *+ *引用数据类型

7种数据类型numberstringbooleanundefinednullSymbolObject (Object、Array、Function)

typeof 运算符

7 种类型:undefinedstringnumberbooleanobjectfunctionsymbol(ES6提供的新的类型)

注意:typeof null // object

typeof 运算符 只能 区分 值类型 的 类型,对于引用类型的 对象数组 区分不出来

mark

变量计算

这个主要针对值类型- 强制类型转换

4种强制类型转换:

  • 字符串拼接
  • == 运算符
  • if 语句
  • 逻辑运算

字符串拼接

mark

== 运算符

mark

if 语句

if 语句

false 情况0NaN'<空字符串>'nullundefinefalse

mark

逻辑运算符

mark

何时使用 === 和 ==

何时使用 === 和 ==?

解答:参考jQuery源码中推荐的写法,除了判断对象属性是否为空看是否函数的参数为空 ** 的情况 ,其余的都用 **===

== : 只进行值的比较

=== : 不仅进行值得比较,还要进行数据类型的比较

mark

JS中的内置函数

JS中的内置函数的作用

mark

JS按存储方式区分变量类型

参考1

参考2

基本类型的值是不可变的

mark

如何理解 JSON

JS 内置对象,Math也是内置对象

注意:JSON 既是一个JS 内置对象,也是一种 数据格式

mark

2-2 原型和原型链

2-2 原型和原型链

题目

知识点

解答

题目

  • 如何准确判断一个变量是 数组类型
  • 写一个原型链继承的例子
  • 描述 new 一个对象的过程
  • zepto (或其他框架) 源码中如何使用原型链

知识点

  • 构造函数
  • 构造函数-扩展
  • 原型规则和示例
  • 原型链
  • instanceof

构造函数

函数名 习惯 第一个字母大写( 高级程序员规范)

mark

构造函数扩展

构造函数扩展

函数扩展 ---- 语法糖

mark

5条原型规则和示例

5条原型规则

原型规则 是学习 原型链 的基础

5条原型规则 :

  1. 所有的 引用类型 (对象,数组,函数),都具有对象特性,即可 自由扩展 属性(除了null以外)。

  2. 所有的 引用类型 (对象,数组,函数),都有一个__proto__隐式原型 )属性,属性值都是一个普通对象。

    mark

  3. 所有的函数都有一个prototype显示原型 )属性,属性值是一个普通对象。

  4. 所有的引用类型(对象,数组,函数),__proto__属性值指向它的构造函数的prototype属性值。

    mark

  5. 当试图得到一个引用类型的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中去找。

示例

mark

循环自身的属性:

mark

原型链

这种搜索的轨迹,形似一条长链, 又因 prototype 在这个游戏规则中充当链接的作用,于是我们把这种实例与原型的链条称作 原型链

参考

mark

mark

instanceof

用于 判断 引用类型 属于哪个 构造函数的方法

*instanceof 运算符 * 用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置

参看MDN

mark

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 一个对象的过程

    mark

  • zepto (或其他框架) 源码中如何使用原型链

    mark

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

  • 作用域

  • 作用域链

  • 闭包

执行上下文

执行上下文

mark

mark

this

this要在 执行时 才能确定值,定义时 无法确认

mark

mark

块级作用域

任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

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语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 定义在一个函数内部的函数

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

参考阮一峰的网络日志

闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

实际开发中闭包的应用

实际开发中闭包的应用

mark

解题

  • 说一下对变量提升的理解

    • 变量定义
    • 函数声明 ( 注意和 函数表达式 的区别)
  • 创建10个 a 标签,点击的时候弹出来对应的序号

    mark

    mark

  • 如何理解作用域

    • 自由变量
    • 作用域连,即自由变量的查找
    • 闭包的两个场景
  • 实际开发中闭包的应用

    mark

精彩评论:
上一页 下一页