前端面试题(JavaSscipt 变量与原型链)

226 阅读6分钟

变量类型和计算

知识点:

  • 变量类型

值类型:数值,逻辑值,字符串等如200,在内存中为与变量储存在一起。赋值给其他变量后,本身不受影响

var a = 100
var b = a
a = 200
console.log(b)//100

引用类型:对象,函数,数组:值储存在单独的内存区域,变量通过指针指向内存区域。赋值给其他值后,其他值变更自身也变更。

var a = {age:20}
var b = a
b.age = 21
console.log(a.age) //21
  • 变量计算——强制类型转换

字符串拼接

var a=100 +10 //110
var b=100 +'10' //10010

== 运算符

100 == '100' //true
0 == '' //true
null == undefined //true

if语句

var a = true
if(a){
 //执行
}
var b = 100
if(b) {
//执行
}
var c= ''
if (c) {
  //不执行 c转换为false
}

逻辑运算

console.log(10 && 0) //0
console.log('' || 'abc') //'abc'
console.log(!window.abc) //true

// 判断一个变量会被当成 true 还是 false
var a = 100
console.log(!!a)

题目:

1、JS中使用typeof能得到哪些类型?

// 值类型
typrof undefined //undefined
typeof 'abc' //string
typeof 123 //number
typrof true //boolean
typeof Symbol('s') // symbol
// 能判断函数
typeof console.log //function
typeof function(){} // function
// 能识别为引用类型(不能再继续识别)
typeof {} //object
typeof [] //object
typeof null //object 

2、何时使用===?何时使用==?

//==标准用法(只有这种情况用==)
if(obj.a == null){
// 这里相当于 obj.a ===null || obj,a === undefined ,简写形式
// 这里是jquery 源码中的推荐写法
}
//其他情况全用===

3、JS中有哪些内置函数?

// 内置函数 —— 数据封装对象

Object 
Array
Boolean
Number
String
Function
Date
RegExp //正值
Error

//内置对象
Math
JSON

4、JS变量按照储存方式区分为哪些类型,并描述其特点?

值类型:数值,逻辑值,字符串等如200,在内存中为与变量储存在一起。赋值给其他变量后,本身不受影响

var a = 100
var b = a
a = 200
console.log(b)//100

引用类型:对象,函数,数组:值储存在单独的内存区域,变量通过指针指向内存区域。赋值给其他值后,其他值变更自身也变更。

var a = {age:20}
var b = a
b.age = 21
console.log(a.age) //21

5、如何理解JSON?

// JSON 是一个JS 内置对象
// JSON 是一种数据格式
JSON.string ({a:10,b:20}) //对象转换为字符串
JSON.parse('{'a':10,'b':20}') // 字符串转换为对象

6、深拷贝

/**
 * 深拷贝
 */

const obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c']
}

const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)
console.log(obj1.arr[0])

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }

    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

    // 返回结果
    return result
}

7、 falsely 变量和truely变量 

//除此以外都是truly变量
!!0 === false
!!NaN === false
!!'' ===false
!!null ===false
!!undefined === false
!!false === false

if判断时判断的是falsely 变量和truely变量,与或非判断是也是判断falsely变量和truely变量

8、字符串拼接

const a = 100+10 //110
const b = 100+"10" //"10010"
const c = true + 10 // "true10"

原型和原型链

知识点

ES6

1、 类

class 定义类

constructor 构建属性

类中可以定义方法

// 类
class Student {
    constructor(name, number) {
        this.name = name
        this.number = number
        // this.gender = 'male'
    }
    sayHi() {
        console.log(
            `姓名 ${this.name} ,学号 ${this.number}`
        )
        // console.log(
        //     '姓名 ' + this.name + ' ,学号 ' + this.number
        // )
    }
    // study() {

    // }
}

// 通过类 new 对象/实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()

const madongmei = new Student('马冬梅', 101)
console.log(madongmei.name)
console.log(madongmei.number)
madongmei.sayHi()

2、继承

extend 定义继承的父类

super 执行父类的构造函数

// 父类
class People {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat something`)
    }
}

// 子类
class Student extends People {
    constructor(name, number) {
        super(name) // 交给父类
        this.number = number
    }
    sayHi() {
        console.log(`姓名 ${this.name} 学号 ${this.number}`)
    }
}

// 子类
class Teacher extends People {
    constructor(name, major) {
        super(name)
        this.major = major
    }
    teach() {
        console.log(`${this.name} 教授 ${this.major}`)
    }
}

// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
xialuo.eat()

// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name)
console.log(wanglaoshi.major)
wanglaoshi.teach()
wanglaoshi.eat()

原型

// class 实际上是函数,可见是语法糖
typeof People //'function'
typeof Student // 'function'

//隐式原型和显示原型
console.log( xialuo.__proto__)
console.log( Student.prototype )
console.log( xialuo.__proto__ === Studnet.prototype ) 

原型关系

  1. 每个class 都有显示原型prototype
  2. 每个实例都有隐式原型__proto__
  3. 实例的__proto__指向对应class的prototype

基于原型的执行规则

  1. 获取属性xialuo.name 或执行方法·xialuo.sayhi()时
  2. 先在自身属性和方法寻找
  3. 如果找不到则自动去__proto__中查找

 原型链

顶层的JS自带prototype中的__proto__ 始终指向null 到此为止!

问题:

1、如何准确判断一个变量是不是数组

instanceof

语法

a instanceof Array

2、手写简易jquery,考虑插件和扩展性

class jQuery {
    constructor(selector) {
        const result = document.querySelectorAll(selector)
        const length = result.length
        for (let i = 0; i < length; i++) {
            this[i] = result[i]
        }
        this.length = length
        this.selector = selector
    }
    get(index) {
        return this[index]
    }
    each(fn) {
        for (let i = 0; i < this.length; i++) {
            const elem = this[i]
            fn(elem)
        }
    }
    on(type, fn) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
    // 扩展很多 DOM API
}

// 插件
jQuery.prototype.dialog = function (info) {
    alert(info)
}

// “造轮子”
class myJQuery extends jQuery {
    constructor(selector) {
        super(selector)
    }
    // 扩展自己的方法
    addClass(className) {

    }
    style(data) {
        
    }
}

// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))

ES5

  • 构造函数

    function Foo(name,age) {
        this.name = name
        this.age = age
        this.class = 'class-1'
        //retrun this //函数默认执行这个操作
    }
    var f = new Foo('zhangsan',20)
    // new 时 this 会先变成空对象
    // var f1 = new Foo('lisi',22) // 创建多个对象
    
  • 构造函数-扩展

    var a = {} 其实是 var a = new Object() 的语法糖
    var a = [] 其实是 var a = new Array() 的语法糖
    function Foo(){...} 其实是 var Foo = new Function(...)
    使用instanceof 判断一个函数是否是一个变量的构造函数 
    
  • 原型规则和示例

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

var obj = {};obj.a = 100;
var arr = []; arr.a =100;
function fn() {}
fn.a =100;

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

console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);

3、所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通的对象

console.log(fn.prototype)

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

console.log(obj.__proto__ === Object.prototype)

5、当识图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么回去它的__proto__(即它的构造函数的prototype)中寻找。

// 构造函数
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() //可以运行
f.alertName() //找到在prototype 中定义的属性可以运行


//遍历注意事项
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() //可以运行
    f.alertName() //找到在prototype 中定义的属性可以运行
    f.toString()  //要去 f.__proto__.__proto__中去查找 找不到返回undefined
    

查找过程如图所示

  • instanceof

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

f instancsof Foo 的判断逻辑是:
f 的__proto__一层一层往上,能否对应到Foo.prototype

题目

1、如何准确判断一个变量是*数组类型

var arr = []
arr instanceof Array //true
typrof arr // 结果object,   typeof 是无法判断是否是数组的

2、写一个原型链继承的例子

//理解用实例,面试最好不要用
// 动物
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()


//实用例子 DOM查询
function Elem(id) {
    this.elem = document.getElementId(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.addEventListener(type,fn)
    return this
}

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

3、描述 new 一个对象的过程

  1. 创建一个新对象

  2. this指向这个对象

  3. 执行代码,即对this赋值

  4. 返回this

    function Foo(name,age) {
        this.name = name
        this.age = age
        this.class = 'class-1'
        //retrun this //函数默认执行这个操作
    }
    var f = new Foo('zhangsan',20)
    // new 时 this 会先变成空对象
    // var f1 = new Foo('lisi',22) // 创建多个对象
    

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