博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端面试系列-JS 基础知识点
阅读量:6966 次
发布时间:2019-06-27

本文共 5814 字,大约阅读时间需要 19 分钟。

原始类型有哪几种?null 是对象嘛?基本数据类型和引用数据类型在存储上的区别?

六种原始数据类型(原始类型存储的都是值,是没有函数可以调用的),"1".toString() 为什么能够调用,因为"1"会强制转换为String类型也就是对象类型(装箱操作)。 JS 的 number 类型是浮点类型的,在使用中会遇到某些 Bug,比如 0.1 + 0.2 !== 0.3

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol(es6新增)

null也并不是对象,虽然typeof null 为object。但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object

基本数据类型是存储在栈中的值,而引用数据类型是存在栈中的引用,查询的时候通过这个栈中的引用来查找对应中的堆中的具体的值。

0.1 + 0.2 !== 0.3

想要知道为什么0.1+0.2!=0.3 ,首先知道计算机用位来储存及处理数据,每一个二进制数(二进制串)都一一对应一个十进制数。十进制整数转二进制方法:除2取余;十进制小数转二进制方法:乘2取整。所以十进制0.1转化为2进制乘以2的过程就是

0.1 * 2 = 0.2 取 0 0.2 * 2 = 0.4 取 0 0.4 * 2 = 0.8 取 0 0.8 * 2 = 1.6 取1 0.6 * 2 = 1.2 取 1 0.2 * 2 = 0.4 取 0
所以0.1的二进制格式就是0.00011 无线循环,但计算机内存有限 在某个精度点直接舍弃。当然,代价就是,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1 所以一开始计算之前0.1 就是不准确的 所以最终的结果0.1+0.2!=0.3

对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?

原始类型存储的是具体的值,对象类型存储的是地址的引用(指针)。

参数是对象的情况。

function test(person) {      person.age = 26      person = {        name: 'yyy',        age: 30      }          return person    }    const p1 = {      name: 'yck',      age: 25    }    const p2 = test(p1)    console.log(p1) // -> ?    console.log(p2) // -> ?复制代码

过程如下图:

typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?

  • typeof对于原始值,除了null都能判断出正确的类型。
  • typeof对于对象来说,除了函数都会显示object,所以说typeof不能准备判断类型
  • instanceof可以判断一个实例是否属于当前类,因此可以判断类型
const Person = function() {}    const p1 = new Person()    p1 instanceof Person // true        var str = 'hello world'    str instanceof String // false        var str1 = new String('hello world')    str1 instanceof String // true            //对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的    class PrimitiveString {      static [Symbol.hasInstance](x) {        return typeof x === 'string'      }    }    console.log('hello world' instanceof PrimitiveString) // true复制代码

类型转换

  • undefined, null, false, NaN, '', 0, -0 为false外,其余都为true
  • 对象转换为原始类型会调用valueof()和toString()(不同的运算两者的顺序还会有所不同),当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。
let a = {      valueOf() {        return 0      },      toString() {        return '1'      },      [Symbol.toPrimitive]() {        return 2      }    }    1 + a // => 3复制代码
  • 'a'++'b' // 'aNaN' ,因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型
  • 比较运算符
    • 如果是对象,就通过 toPrimitive 转换对象
    • 如果是字符串,就通过 unicode 字符索引来比较
let a = {      valueOf() {        return 0      },      toString() {        return '1'      }    }    a > -1 // true复制代码

this指向问题

function foo() {      console.log(this.a)    }    var a = 1    foo()        const obj = {      a: 2,      foo: foo    }    obj.foo()        const c = new foo()复制代码
  • foo() 无论在什么环境下执行,都是window
  • obj.foo() 看是谁调用的foo ,this就是谁(也就是obj)
  • new 出来的this 指向当前实例(也就是c)
  • 箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this
  • 改变this的指向(call、apply、bind),bind多次的this只取决于第一次的bind。
// 自己实现bind    Function.prototype.testBind = function(that){    var _this = this,        /*        *由于参数的不确定性,统一用arguments来处理,这里的arguments只是一个类数组对象,有length属性        *可以用数组的slice方法转化成标准格式数组,除了作用域对象that以外,        *后面的所有参数都需要作为数组参数传递        *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1)        */        slice = Array.prototype.slice,        args = slice.apply(arguments,[1]);    //返回函数        return function(){        //apply绑定作用域,进行参数传递        return _this.apply(that,args)    }    }复制代码
  • this的优先级(你不知道的js很清楚了讲了这个优先级的问题,这里列一下,从高到低)
    • new 方式
    • bind等函数
    • obj.foo()
    • foo()

== 和 === 有什么区别?([]==![],![] 转换为false,左边先转换为字符串"",最后两者都转换为0)

  • == 会进行隐式的类型转换(简单的理解最终两个类型不相同会转换为数字进行比较,如果到字符串级别可以比较了就不会再向下进行转换)
    • 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
    • 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
    • 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
  • 对于 === 来说就简单多了,就是判断两者类型和值是否相同。

闭包(经典)(函数能够记住访问其所在的词法作用域(你不知道的js书中解释))

函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。

经典面试题:循环中使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {      setTimeout(function timer() {        console.log(i)      }, i * 1000)    }    // 6 6 6 6 6        // 解决方法1:闭包    for (var i = 1; i <= 5; i++) {      ;(function(j) {        setTimeout(function timer() {          console.log(j)        }, j * 1000)      })(i)    }        // 解决方法2:setTimeout 的第三个参数,这个参数会被当成 timer 函数的参数传入    for (var i = 1; i <= 5; i++) {      setTimeout(        function timer(j) {          console.log(j)        },        i * 1000,        i      )    }        // 解决方法3:let    for (let i = 1; i <= 5; i++) {      setTimeout(function timer() {        console.log(i)      }, i * 1000)    }复制代码

深浅拷贝及其实现

  • 浅拷贝
    • Object.assign()
    • es6的扩展运算符(...)
  • 深拷贝
    • JSON.parse(JSON.stringify(object)),但是有局限性。
      • 会忽略 undefined
      • 会忽略 symbol
      • 不能序列化函数
      • 不能解决循环引用的对象

let a = {      age: undefined,      sex: Symbol('male'),      jobs: function() {},      name: 'xxx'    }    let b = JSON.parse(JSON.stringify(a))    console.log(b) // {name: "xxx"}复制代码
  • 如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel
function structuralClone(obj) {  return new Promise(resolve => {    const { port1, port2 } = new MessageChannel()    port2.onmessage = ev => resolve(ev.data)    port1.postMessage(obj)  })}var obj = {  a: 1,  b: {    c: 2  }}obj.b.d = obj.b// 注意该方法是异步的// 可以处理 undefined 和循环引用对象const test = async () => {  const clone = await structuralClone(obj)  console.log(clone)}test()复制代码
// 自己实现的deepCopy    function deepClone(obj) {      function isObject(o) {        return (typeof o === 'object' || typeof o === 'function') && o !== null      }          if (!isObject(obj)) {        throw new Error('非对象')      }          let isArray = Array.isArray(obj)      let newObj = isArray ? [...obj] : { ...obj }      Reflect.ownKeys(newObj).forEach(key => {        newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]      })          return newObj    }复制代码

原型

  • Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
  • Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
  • 函数的 prototype 是一个对象
  • 对象的 proto 属性指向原型, proto 将对象和原型连接起来组成了原型链

转载地址:http://flzsl.baihongyu.com/

你可能感兴趣的文章
[原]Jenkins(八)---jenkins构建项目报错时发送错误报告邮件
查看>>
Smart Framework:轻量级 Java Web 框架
查看>>
在win10中使用linux上的bash命令行
查看>>
Eclipse FindBugs插件安装与使用
查看>>
介绍:成为一名 Jenkins 贡献者的旅程
查看>>
java中equals和==的区别
查看>>
还是使用jquery实现的无刷新的文件下载
查看>>
Porteus Kiosk
查看>>
$apply
查看>>
让KVM虚机能使用音箱与麦克风(vnc及ac97)
查看>>
开源还是商用?十大云运维监控工具测评告诉你答案
查看>>
Java两个时间之间差多少秒
查看>>
为读者更有目的性先放出《超容易的Linux系统管理入门书》一书的学习重点
查看>>
android Application.mk文件的APP_MODULES:
查看>>
Nginx配置文件nginx.conf中文详解
查看>>
无锁队列的实现
查看>>
SpringSecurity重写LogoutFilter
查看>>
使用idfc-proguard-maven-plugin混淆优化Jave Web工程二
查看>>
tomcat 设置内存
查看>>
怎么一边敲代码还能一边赚点钱,一字一字敲的,不喜勿喷哈,IOS手机看进来...
查看>>