文章目录

  • 1.原始类型有哪几种?null 是对象嘛?
    • 基本数据类型存在的一些bug
    • null 不是对象类型
  • 2. 对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?
    • 基本数据类型为栈存储吗,基本数据类型为堆存储
    • 函数参数的传递(按值传递)
  • 3. typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?
    • typeof 并不能准确判断变量到底是什么类型
    • instanceof 能判断一个对象的正确类型,因为内部机制是通过原型链来判断的
  • 4. 类型转换
    • 1. 转换为boolean
    • 2. 对象转基本数据类型
    • 3. 四则运算符
    • 4. 比较运算符
    • 装箱转换(把基本类型转换为对应的对象)
    • 拆箱转换(规定ToPrimitive函数,对象类型到基本类型)
  • 5. 如何正确判断 this?箭头函数的 this 是什么?
    • new 运算接受一个构造器和一组调用参数,实际上做的事情
    • 箭头函数的this
    • 如果对一个函数进行多次 bind,那么上下文会是什么呢?
    • 发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。
  • 6. == 和===有什么区别?
    • 对于 [] == ![]
  • 7. 什么是闭包?
    • 使用闭包解决 `var` 定义函数的问题
  • 8. 什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?
    • 浅拷贝 Object.assign
    • 深拷贝`JSON.parse(JSON.stringify(object))`
  • 9. 如何理解原型?如何理解原型链?
    • 为什么 obj 可以访问到 valueOf 函数,就是因为 obj 通过原型链找到了 valueOf 函数。

1.原始类型有哪几种?null 是对象嘛?

原始类型即为基本数据类型:(6种)

  • null
  • undefined
  • string
  • number
  • boolean
  • symbol

基本数据类型存储的都是值,没有函数可以调用(例undefined.toString()

基本数据类型存在的一些bug

  1. number 为浮点类型
    在使用中会遇到某些 Bug,比如 0.1 + 0.2 !== 0.3
  2. string 类型是不可变的
    无论你在 string 类型上调用何种方法,都不会对值有改变。

null 不是对象类型

虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。

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

基本数据类型为栈存储吗,基本数据类型为堆存储

  • 基本数据类型可以操作保存在变量中实际的值(按值访问)
  • 引用数据类型的值是保存在内存中的对象,实际上是在操作对象的引用而不是实际的对象(按引用访问:当复制保存某个对象的变量时,实际上操作的是对象的引用,但是在为对象添加属性时,操作的是实际的对象)
  1. 如果对象不被销毁或者属性不被删除,则这个属性将一直保存‘’
  2. 不能给基本类型的值添加属性(只能给引用类型值动态添加属性)

函数参数的传递(按值传递)

  • 基本类型值的传递,如同基本类型的变量复制一样
    被传递的值会被赋给一个局部变量,即arguments 对象中一个元素
  • 引用类型值的传递,如同引用类型的变量复制一样
    会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部
    function test(person) {person.age = 26person = {name: 'yyy',age: 30}return person}const p1 = {name: 'yck',age: 25}//  console.log(p1)// {name: "yck", age: 25}//  console.log(test(p1))  //{name: "yyy", age: 30}const p2 = test(p1)console.log(p1) // yck 26console.log(p2) // yyy 30
  1. 首先要知道 p1为一个全局对象,所以当被赋值为26 时,则全局的p1.age也会被改了
  2. 当为p1 重新分配一个对象时,就会重新建立引用,则得到p2的值

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

typeof 并不能准确判断变量到底是什么类型
  1. 对于基本数据类型来说,除了 null 都可以显示正确的类型
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
  1. typeof 对于对象来说,除了函数都会显示 object
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
instanceof 能判断一个对象的正确类型,因为内部机制是通过原型链来判断的
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // truevar str = 'hello world'
str instanceof String // falsevar str1 = new String('hello world')
str1 instanceof String // true  string对象

对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的

class PrimitiveString {static [Symbol.hasInstance](x) {return typeof x === 'string'}
}
console.log('hello world' instanceof PrimitiveString) // true

Symbol.hasInstance,是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof ‘hello world’ === ‘string’,所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

4. 类型转换

在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

1. 转换为boolean

在条件判断时,除了 undefined, null, false, NaN, ‘’, 0, -0,其他所有值都转为 true,包括所有对象。

2. 对象转基本数据类型

调用内置的[[ToPrimitive]]函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是基本数据类型了,就不需要转换了
  • 调用x.valueOf()x.toString()

重写 Symbol.toPrimitive ,该方法在转基本数据类型时调用优先级最高。

let a = {valueOf() {return 0},toString() {return '1'},[Symbol.toPrimitive]() {return 2}
}
1 + a // => 3

在这里总结一下对于引用数据类型的toString和valueof 方法的结果:

引用数据类型 toString tolocalString valueof
Array 返回数组中每个值的字符串形式拼接成的一个以逗号分隔的字符串 创建一个数组值以逗号分隔的字符串 返回数组
Date 返回带有时区信息的日期和时间 按照与浏览器设置的地区响应的格式返回日期和时间 不返回字符串,返回日期的毫秒表示
ReqExp 返回正则表达式的字面量,与创建正则表达式的方式无关 返回正则表达式的字面量,与创建正则表达式的方式无关 返回正则表达式本身
function 返回函数代码 返回函数代码 返回函数代码

总结一下基本包装类型的toString和valueof 方法的结果:
基本包装类型
没当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而能够调用一些方法去操作这些数据。
基本包装类型的实例调用typeof 时返回“object”

引用类型和基本包装类型的区别
对象的生存期

  • 使用new 操作符创建的引用类型的实例,在执行流离开当前作用域的之前都一直保存在内存中
  • 自动创建的基本包装类型对象,只存在于一行代码的执行瞬间,然后立即被销毁(表明不能在运行时为基本类型值添加属性和方法)
基本包装类型 toString tolocalString valueof
Boolean "true" "false"字符串 true,false 的基本类型值
Number 字符串形式的值 字符串形式的值 数值(基本类型)
String 基本字符串值 基本字符串值 基本字符串值

3. 四则运算符

  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11' 转换为字符串
true + true // 2
4 + [1,2,3] // "41,2,3" 转换为字符串1,2,3

另外对于加法还需要注意这个表达式'a' + + 'b'

'a' + + 'b' // -> "aNaN"

因为+ 'b'==NaN 所以结果为aNaN
在一些代码中通过 + ‘1’ 的形式来快速获取 number 类型。
那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN

4. 比较运算符

  1. 如果是对象,就通过 toPrimitive 转换对象
  2. 如果是字符串,就通过 unicode 字符索引来比较
let a = {valueOf() {return 0},toString() {return '1'}
}
a > -1 // true

在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。


装箱转换(把基本类型转换为对应的对象)

使用内置的object 函数,可以在js 内部代码中显式调用装箱能力

    var symbolObject = Object(Symbol("a"));console.log(typeof symbolObject); //objectconsole.log(symbolObject instanceof Symbol); //trueconsole.log(symbolObject.constructor == Symbol); //true

每个对象都有class 属性,这些属性可以通过 Object.prototype.toString 获取

    var symbolObject = Object(Symbol("a"));console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

在 JavaScript 中,没有任何方法可以更改私有的 class 属性,因此
Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,比instanceof 更加准确

但需要注意的是,call 本身会产生装箱操作,所以需要配合 typeof 来区分基本类型还是对象类型

拆箱转换(规定ToPrimitive函数,对象类型到基本类型)

对象到String 和 Number 的转换都遵循“先拆箱再转换”的规则,通过拆箱转换,把对象变成基本类型,再从基本类型转换为对于的String 和 Number

拆箱转换会尝试调用valueOf 和toString 来获得拆箱后的基本类型,如果 valueOf 和toString 都不存在,或者没有返回基本类型,则会产生错误 TypeError

    var o = {valueOf : () => {console.log("valueOf"); return {}},toString : () => {console.log("toString"); return {}}}o * 2// valueOf// toString// TypeError

可以看到这个对象o 在进行o*2 运算的时候,先执行了了valueOf 然后执行了toString 最后抛出了错误,说明这个拆箱转换失败了。

string的拆箱转换会优先调用toString

    var o = {valueOf : () => {console.log("valueOf"); return {}},toString : () => {console.log("toString"); return {}}}String(o)// toString// valueOf// TypeError

ES6中还允许对象通过显示指定@@toPrimitive Symbol 来覆盖原有的行为。

    var o = {valueOf : () => {console.log("valueOf"); return {}},toString : () => {console.log("toString"); return {}}}o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}console.log(o + "")// toPrimitive// hello

5. 如何正确判断 this?箭头函数的 this 是什么?

function foo() {console.log(this.a)
}
var a = 1
foo()const obj = {a: 2,foo: foo
}
obj.foo()const c = new foo()
  • 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
  • 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
  • 对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this

new 运算接受一个构造器和一组调用参数,实际上做的事情

  1. 以构造器的prototype 属性为原型,创建新对象
  2. 将this 和调用参数传给构造器
  3. 如果构造器返回的是对象,则返回第一步创建的对象

new 这样的行为,在客观上提供了两种方式:

  • 在构造器中添加属性
  • 在构造器中的prototype 的属性中添加属性
  1. 直接在构造器中修改了this ,给this 添加属性:

function c1(){this.p1 = 1;this.p2 = function(){console.log(this.p1);}
}
var o1 = new c1;
o1.p2();function c2(){}
c2.prototype.p1 = 1;
c2.prototype.p2 = function(){console.log(this.p1);
}var o2 = new c2;
o2.p2();
  1. 修改构造器中的prototype 属性指向的对象,他是从这个构造器构造出来的所有对象的原型:

箭头函数的this

首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。

function a() {return () => {return () => {console.log(this)}}
}
console.log(a()()())

在这个例子中,因为包裹箭头函数的第一个普通函数是 a,所以此时的 this 是 window。另外对箭头函数使用 bind 这类函数是无效的

如果对一个函数进行多次 bind,那么上下文会是什么呢?

let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?

可以转换为如下形式:

// fn.bind().bind(a) 等于
let fn2 = function fn1() {return function() {return fn.apply()}.apply(a)
}
fn2()

可以从上述代码中发现,不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window。

let a = { name: 'yck' }
function foo() {console.log(this.name)
}
foo.bind(a)() // => 'yck'

发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。

  1. new
  2. bind
  3. obj.foo()
  4. foo 的调用方式

同时箭头函数的this 一旦被绑定,就不会被任何方式再改变

6. == 和===有什么区别?

  • 对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换
  • 对于 === 来说,首先会进行类型判定,如果相同则再进行判定值

假如我们需要对x和y判断是否相同,需要以下流程

  1. 首先会判断两者类型是否相同。相同的话就是比大小了
  2. 类型不相同的话,那么就会进行类型转换
  3. 会先判断是否在对比 null 和 undefined,是的话就会返回 true
  4. 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
  5. 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
  6. 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断

对于 [] == ![]

console.log(typeof([]==![]))//boolean

7. 什么是闭包?

有权访问函数作用域变量的函数

     function out() {var num = 1;return function  (n){return (n + num)}}var a = out()console.log(a(1)) //2console.log(out()(2))  //3
     function outer() {var num = 1;function inner () {var n = 2;alert(n + num)  }return inner;}outer()();//3

使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i) //结果输出5个6}, i * 1000)
}

因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。

  1. 使用闭包的方式
for (var i = 1; i <= 5; i++) {;(function(j) {setTimeout(function timer() {console.log(j)  // 12345}, j * 1000)})(i)
}
  1. 第二种就是使用 setTimeout 的第三个参数,这个参数会被当成 timer 函数的参数传入。
for (var i = 1; i <= 5; i++) {setTimeout(function timer(j) {console.log(j)  //逐步输出12345},i * 1000,i)
}
  1. 使用let
for (let i = 1; i <= 5; i++) {setTimeout(function timer() {console.log(i) //12345}, i * 1000)
}

8. 什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?

浅拷贝解决引用数据类型的问题(只有一层),深拷贝解决值中还存在对象的问题。

浅拷贝 Object.assign

对于引用数据类型会存在下面的问题(解决办法:浅拷贝)

let a = {age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
  1. Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
let a = {age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
  1. 通过展开运算符... 来实现浅拷贝
let a = {age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1

浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。

深拷贝JSON.parse(JSON.stringify(object))

拷贝值中存在对象时,需要进行深拷贝

let a = {age: 1,jobs: {first: 'FE'}}let b = aa.jobs.first = 'native'console.log(b.jobs.first) // native
let a = {age: 1,jobs: {first: 'FE'}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

局限性:

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数
  • 不能解决循环引用的对象

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

9. 如何理解原型?如何理解原型链?

每个 JS 对象都有 __proto__ 属性,这个属性指向了原型,浏览器在早期为了让我们访问到内部属性 [[prototype]]来实现的一个东西。

原型也是一个对象,并且这个对象中包含了很多函数,所以我们可以得出一个结论:对于 obj 来说,可以通过 __proto__ 找到一个原型对象,在该对象中定义了很多函数让我们来使用。


在上面的图中我们还可以发现一个 constructor 属性,也就是构造函数原型的 constructor 属性指向构造函数,构造函数又通过 prototype 属性指回原型
Function.prototype.bind() 就没有这个属性。

原型链就是多个对象通过 __proto__ 的方式连接了起来

为什么 obj 可以访问到 valueOf 函数,就是因为 obj 通过原型链找到了 valueOf 函数。

总结:

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

前端知识总结之基础知识相关推荐

  1. 公共基础知识计算机,公共基础知识计算机基础知识试题

    计算机基础知识是公共基础知识考试的组成成分之一,以下是由学习啦小编整理关于共基础知识计算机基础知识试题的内容,希望大家喜欢! 公共基础知识计算机基础知识试题 1.CPU的主要功能是进行( ). A.算 ...

  2. 【基础知识】RMAN基础知识-Part2

    接上一篇[基础知识]RMAN基础知识-Part1 利用两篇将之前学的内容整理出来,又做了一遍实验,常用不常用的都有.有很多可以深挖的点,后面慢慢搞再 一.配置备份集-备份片-压缩-加密 1.配置备份集 ...

  3. python基础知识下载_python基础知识(实用)

    文档来源为 : 从网络收集整理 .word 版本可编辑 . 欢迎下载支持 . 1 Python 基础知识 目录 Python 基础知识 ................................ ...

  4. 计算机应用基础的知识探索,计算机应用基础知识探索

    计算机应用基础知识探索Tag内容描述: 1.计算机应用基础知识计算机应用基础知识点计算机应用基础知识话题计算机应用基础知识计算机基础知识电子商务单击第一章计算机基础知识1.1946年2月15日世界上第 ...

  5. 计算机知识txt,计算机基础知识txt.doc

    计算机基础知识txt 计算机基础知识txt 1计算机基础知识 本章将介绍计算机的发展与应用,信息处理的基本知识,计算机硬件系统和软件系统的基本知识,微机的使用,多媒体技术知识及计算机病毒的知识等内容, ...

  6. 计算机软件硬件基础知识,计算机软硬件基础知识

    计算机软硬件基础知识 ▲电脑硬件基础知识 ★1.了解电脑的基本组成 一般我们看到的电脑都是由:主机(主要部分).输出设备(显示器).输入设备(键盘和鼠标)三大件组成.而主机是 电脑的主体,在主机箱中有 ...

  7. 知识图谱从入门到应用——知识图谱的基础知识

    分类目录:<知识图谱从入门到应用>总目录 相关文章: · 知识图谱的基础知识 · 知识图谱的发展 · 知识图谱的应用 · 知识图谱的技术结构 知识图谱是有学识的人工智能 早期的人工智能有很 ...

  8. 关于学习密码学知识的一些基础知识( trapdoor function)

    关于学习密码学知识的一些基础知识( trapdoor function) trapdoor function 陷门函数:正向计算是很容易的,但若要有效的执行反向计算则必须要知道一些secret/key ...

  9. 内网安全学习从入门到入狱-知识-内网基础知识

    内网安全学习从入门到入狱-知识-内网基础知识 文章目录 内网安全学习从入门到入狱-知识-内网基础知识 内网渗透基础 基本的名词介绍 工作组 域 活动目录(AD) DC 域控制器(DC) DNS域名服务 ...

  10. 网站前端开发必会基础知识有哪些?

    自己工作做得好好的,怎么非要去搞前端?" 很多人离职的时候,可能印象最深的就是爸妈每天说的这句话.起因很简单,就是自己辞了爸妈眼中的"铁饭碗". 他也是如此,毅然辞去了一 ...

最新文章

  1. Linux Kernel Development——列出系统中所有的进程
  2. vue history模式Nginx配置
  3. PHP中unset,array_splice删除数组中元素的区别
  4. DatagridView 常用功能代码
  5. OpenShift Security (8) - 安装并运行 DevSecOps 应用
  6. xcode11 新功能_Xcode 11功能
  7. spring和spring_Spring MVCGradle
  8. linux程序文本,Linux之文本处理
  9. 下轮“双一流”将有高校下车?教育部最新说法来了!
  10. 人行征信报告(上)——一代征信报告的变量梳理
  11. 高效工作的浏览器插件
  12. ftp.proxy 代理服务器搭建
  13. html页面退格代码,HTML中的退格键
  14. 随笔-给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和
  15. STM32输出PWM波形并实现呼吸灯
  16. 系统集成项目管理工程师——挣值管理(PV、EV、AC、SV、CV、SPI、CPI)
  17. 知乎9.7万人围观热帖:“同事被富婆包养了,我哪里不如他?”
  18. SAR ADC系列16:CDAC上机实践+作业
  19. python学习笔记连载
  20. 提取.bank音频包。 Extract .bank audio files

热门文章

  1. grasps什么意思中文_grasps是什么意思_grasps怎么读_grasps翻译_用法_发音_词组_同反义词_抓住( grasp的第三人称单数 )-新东方在线英语词典...
  2. Neuroink的8个疯狂梦想
  3. 南京邮电大学网络攻防平台WriteUP——WEB(上)
  4. python游戏彩蛋_用python写游戏之2D跑酷游戏(一)
  5. 我挑了 10 本数据库类好书,来送你
  6. android 类对象的存储,android - 以共享首选项存储和检索类对象
  7. [SugerTangYL] 简易电子时钟Verilog设计
  8. python广深地区房价数据的爬取与分析
  9. Golang学习(二十四)家庭记账项目
  10. 用java实现matlab的随机函数randsrc(m,n,[alphabet; prob])