前端面试:JS基础知识梳理

5,184 阅读8分钟

题目一

  1. JS中使用typeof 能得到哪些类型
  2. 何时使用 === 何时使用==
  3. JS中有哪些内置函数
  4. JS变量按照存储方式区分为哪些类型,并描述其特点
  5. 如何理解JSON

JS按照存储方式分类分为哪几种类型:

  • 引用类型:对象、数组、函数
    • 特点:不限制属性,指针指向共同存储块,节省内存空间
  • 值类型: 布尔、num、字符串、underfind
    • 每个变量单独一个固定存储块

typeof运算符

  • typeof 只能区分值类型数据

    • 引用类型的话只能分辨出函数(function),比较特殊
    typeof undefined  //undefined
    typeof 'abc'      //string
    typeof 123        //number
    typeof true       //boolean
    typeof {}         //object
    typeof []         //object
    typeof null       //object
    typeof console.log //function
    

值类型的变量计算:强制类型转换计算

  • 字符串拼接
  • 双等好==
  • if语句
  • 逻辑运算符 (&& 、 || 、 !)

何时用 === 何时用 ==

if(obj.a == null){
    //这里相当于obj.a === null || obj.a=== underfind,简写形式
    //这是jq源码中推荐的写法
    //除了这个的判断,其余都建议使用===
}

js中的内置函数有哪些

  • Object
  • Array
  • Boolean
  • Number
  • String
  • Function
  • Date
  • RegExp
  • Error
  • 知道这个的用处就是为了今后学习构造函数使用

什么是json

//json 是js中的对象,类似Math,同时也是一种数据格式
// js给他内置了两个方法
JSON.Stringify({a:1,b:2})  //json转化为字符串
JSON.parse('{"a":1,"b":2}') //字符串转为json


题目二

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

构造函数

function Per(name,age){
    this.name = name;
    this.age = age;
    //return this  默认会返回this,这个this其实是一个对象
}
var a = new Per('zhangsan',20);
// 构造函数就类似一个模板,把一些相同属性和方法统一
// 代表着具有相同属性和方法的类的聚合
// 引用类型的值都具有构造函数,举例:
var a = {} //其实是var a = new Object()的语法糖
var a = [] //其实是var a = new Array()的语法糖
function Foo*(){...} //其实是 var Foo = new Function(...)
// 构造函数的首字母大写

原型规则和示例(掌握五个规则)

  • 规则1:所有引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了“null” 意外)
  • 规则2:所有引用类型(数组、对象、函数),都有一个 __proto__(隐式原型)属性,属性值是一个普通的对象
  • 规则3:所有的函数,都有一个prototype (显示原型)属性,属性值是一个普通对象
  • 规则4: 所有引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的“prototype” 属性值
  • 规则5:当试图得到一个对象(引用类型)的属性的时候,如果这个对象没有这个属性,那么会去它的 __proto__(隐式原型)(即它的构造函数的prototype)中寻找
//举例:对比规则1
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;

//举例:对比规则2
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);

//举例:对比格则3
console.log(fn.prototype);

//举例:对比规则4
console.log(obj.__proto__ === Object.prototype) //true

//举例:对比规则5
//构造函数
function Foo(name, age) {
    this.name = name
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
//创建示例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
//测试
f.printName()   //'zhangsan'
f.alertName()   //'zhangsan'

循环对象自身的属性

//一般情况,f函数有三个属性,name/printName/alertName
//要想拿到对象的自己的属性
var item;
for (item in f) {
 //高级浏览器已经在 for in 中屏蔽了来自原型的属性
 //但是这里建议大家加上这个判断,保证程序的健壮性
 if (f.hasOwnProperty(item)){
     console.log(item)
 }
}

原型链(可看图)

原型链

function Foo(name, age) {
    this.name = name
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
//创建示例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
//测试
f.printName()   //'zhangsan'
f.alertName()   //'zhangsan'
f.toString() //应该找 f.__proto__.__proto__
//首先找:f有没有toString 属性,没有找 f.__proto__ 即 Foo.prototype
//Foo.prototype是对象,也具有__proto__属性
//Foo.prototype.__proto__ 是指向Object.prototype
//Object.prototype 有toString()属性,截止

//在往上的话,js规定,Objcet.prototype.__proto__ 是null,截止

instanceof

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

    var a = [];
    var b = a instanceof Array; 
    //判断是否是一个数组,引用类型的值用typeof不能判断
    
    //举例
    function Foo(name, age) {
        this.name = name
    }
    Foo.prototype.alertName = function () {
        alert(this.name)
    }
    //创建示例
    var f = new Foo('zhangsan')
    var b = f instanceof Foo //判断f 的构造函数 是不是Foo
    //判断的逻辑是:f的__proto__一层一层往上,能否对应到Foo的prototype
    
    //再试着判断 f instanceof Object, 发现能够找到,也是true
    

写一个原型链继承的例子

//动物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}
//狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}
Dog.prototype = new Animal()
//哈士奇
var hashiqi = new Dog()

//则哈士奇继承了dog的属性,以及bark的属性
//以上是小例子便于理解
//下面写的是贴近实战的例子
*****


描述new一个对象的过程

function Foo(name, age) {
    this.name = name
    this.age = age
    this.class = 'class-1'
    //retuen this  //默认有这个
}
var f = new Foo('zghangsan', 20)

var arr = new Array()
//首先创建一个空对象 this = {}
//this指向这个新的对象
//执行代码,即对this赋值
//返回this,即返回新创建的对象{}

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

  • 阅读源码是高效提高技能的方式
  • 慕课网搜索‘zepto设计和源码分析’,对于面试来说加分
//仿照类似jQuery,或者zepto 写一个简单的原型继承的例子
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.innerHTML
    }
}

Elem.prototype.on = function (type, fn) {
    var elem = this.elem
    elem.adEventListener(type, fn)
    return this
}

var div1 = new Elem('div1')
//console.log(div1.html())
div1.html('<p>htllo world</p>').on('click', function(){
    alert('clicked')
}).html('<p>javascript</p>')



题目三

  1. 说一下对变量提升的理解
  2. 说明this几种不同的使用场景
  3. 创建10个标签,点击的时候弹出来对应的序号
  4. 如何理解作用域
  5. 实际开发中闭包的应用

this

  • this要在执行的时候才能确认值,定义时无法确认
var a = {
    name: 'A',
    fn: function () {
        console.log(this.name)
    }
}
a.fn  //this === a
a.fn.call({name: 'B'}) //this === {name: 'B'}
var f1 = a.fn
f1() //this === window

  • 几种this应用场景

    • 作为构造函数执行
    • 作为对象属性执行
    • 作为普通函数执行
    function fn() {
       console.log(this)  //这个this === window
    }
    fn()
    
    
    • call apply bind

      bind得和函数声明联合一起用,最常用的是call

作用域和闭包

  • 一个函数的父级作用域是看这个函数在哪里定义,不要看在哪里执行

  • 闭包的使用场景,一下情况就是闭包

    • 函数作为返回值

      function F1() {
          var a = 100
          retuen function () {
              console.log(a)  //a是自由变量,父级作用域寻找
          }
      }
      var f1 = F1()
      var a = 200
      f1()   //100
      
      
    • 函数作为参数传递

      function F1() {
          var a = 100
          retuen function () {
              console.log(a) 
          }
      }
      var f1 = fn()
      function F2(fn) {
          var a = 200
          fn()
      }
      F2(f1)  //100
      
      
  • 实际开发中闭包的应用

    //闭包实际应用中主要是用于封装变量、收敛权限
    function isFirstLoad() {
        var _list = []
        return function (id) {
            if (_list.indexOf(id) >= 0) {
                return false
            } else {
                _list.push(id)
                retuen true
            }
        }
    }
    //使用
    var firstLoad = isFirstLoad()
    firstLoad(10) //true
    firstLoad(10) //false
    firstLoad(20) //true
    //闭包意义在于:你在isFirstLoad函数外面,根本不可能修改掉_list的值,把一些功能return出去,数据不能更改
    
    
  • 解释自执行函数

    var i
    for (i = 0; i < 10; i++) {
        //每执行一次循环,传一个i值进去,然后创建一个自执行函数,此时的i就变成函数作用域的值存储,总共创建10个函数即10个函数作用域
        (function(i){
            //函数作用域
            var a = document.createElement('a')
            a.innerHTML = i + '<br>'
            a.addEventListener('click', function (e) {
                e.preventDefault()
                alert(i) //自由变量,要去父作用域取值
            })
            document.body.appendChild(a)
        })(i)
    }
    
    


题目四

  1. 同步和异步区别是什么?分别举一个同步和异步的例子
  2. 一个关于setTimeout的笔试题
  3. 前端使用异步的场景有哪些

异步

  • 什么时候使用异步(需要等待的任务)
    1. 定时任务:setTimeout, setInterval
    2. 网络请求:ajax请求,动态加载
    3. 事件绑定

数组API

  • forEach 遍历所有元素

  • every 判断所有元素是否都符合条件

  • some 判断是否有至少一个元素符合条件

  • sort 数组排序

  • map 对元素重新组装,生成新数组

  • filter 过滤符合条件的元素

    // forEach
    var arr = [1,2,3]
    arr.forEach(function (item, index) {
        //遍历所有元素
        console.log(index, item)
    })
    
    //every
    var arr = [1,2,3]
    var result = arr.every(function (item, index) {
        //判断所有数组元素,都满足一个条件
        if (item < 4) {
            return true
        }
    })
    console.log(result)
    
    //sort
    var arr = [1,4,3,2,5]
    var arr2 = arr.sort(function (a, b) {
        //从小到大排序
        return a - b
        
        //从大到小排序
        //return b - a
    })
    console.log(arr2)
    
    // map
    var arr = [1,2,3,4]
    var arr2 = arr.map(function (item, index) {
        //将元素重新组装返回
        return '<b>' + item + '</b>'
    })
    console.log(arr2)
    
    //filter
    var arr = [1,2,3]
    var arr2 = arr.filter(function (item, index) {
        //通过一个条件过滤数组
        if (item >= 2) {
            return true
        }
    })
    console.log(arr2)
    
    

对象API

  • for in

    var obj = {
        x: 100,
        y: 200,
        z: 300
    }
    var key
    for (key in obj) {
        //hasOwnProperty表示自有的属性
        if (obj.hasOwnProperty(key)) {
            console.log(key, obj[key])
        }
    }
    
    

写一个既能遍历对象也能遍历数组的forEach函数