一、基础知识
1.var与let/const区别
1.作用域不同
var :函数作用域
var定义的变量作用域为包含它函数的局部变量,改变量在函数退出时被销毁。(所以在函数内部用var定义的变量在函数外部是访问不到的)
let和const:块级作用域
let和const定义的变量作用域为包含它的代码块。
2.var声明的变量会变量提升,let和const声明的不会
3.var可以重复声明一个变量,let和const重复声明一个变量会报错,const声明变量的同时要赋初值。
4.let、const声明的全局变量不会成为window对象的属性,var声明的变量会
2.typeof
typeof 检测值 | 返回值 |
---|---|
未初始化、未声明变量 | undefined |
boolean | boolean |
string | string |
number | number |
object、null | object |
function | object |
symbol | symbol |
typeof
原理:
js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息👉
000:对象
010:浮点数
100:字符串
110:布尔
1:整数
but, 对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的。
null:所有机器码均为0
undefined:用 −2^30 整数来表示
所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。
3.instanceof原理
每个 JavaScript 对象均有一个隐式的 proto 原型属性,而显式的原型属性是 prototype。instanceof
原理就是先拿到右边的prototype属性,在依次检查左边原型链上的__prop__属性,直到有相等的就返回true
,直到null还未找到就返回false
。
4.数值转换
显式转换
将非数值转换为数值:
Number()、ParseInt()、ParseFloat()
ParseInt()转换规则: 从第一个非空格字符开始转换
,如果第一个转换的字符不是数值、+、-
则返回naN
,直至字符串末尾,所以空字符串也会返回naN。
ParseFloat()转换规则: 从第一个非空格字符开始转换
,如果第一个转换的字符不是数值、+、-
则返回naN
,直至字符串末尾,所以空字符串也会返回naN,只有第一个小数点是有效的后面的都会忽略。
函数 | 参数 | 参数-->返回值 | true/false | 数值 | null | undefined | 字符串含数值 | 字符串含数值和+、- | 字符串含浮点数 | 字符串含十六进制数 | 空字符串 | 字符串前面的都不含 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Number() | 任何数据类型 | 1/0 | 直接返回 | 0 | naN | 十进制数 | 十进制数 | 浮点数 | 十六进制数 | 0 | naN | |
ParseFloat() | 字符串 | 1/0 | 直接返回 | 0 | naN | 十进制数 | 十进制数 | 浮点数 | 十六进制数 | 0 | naN |
隐式转换
换为字符串:
显式转换
toString():null和undefined除外,每个值都有toString()属性。
string()转型函数:null和undefined返回"null"和"undefined",其他值调用toString()。
隐式转换
用+给一个值加上一个" "
5. for..in 与 for..of
for in 和 for of 的异同点
比较 | for..in | for..of |
---|---|---|
不同点 | 可以遍历普通对象 遍历出数组的原型对象 可以遍历出数组自身属性 遍历出来的值是 key 不可以遍历 map/set 不可以迭代 generators IE 支持 | 不能遍历普通对象 不会遍历出原型对象 不会遍历自身属性 遍历出来的值是 value 可以遍历 map/set 可以迭代generators IE 不支持 |
相同点 | 可以遍历数组 可以 break 中断遍历 | 可以遍历数组 可以 break 中断遍历 |
二、 手写实现函数
1.instanceof
function myInstanceof (left, right) {
const RigPro = right.prototype
while (true) {
if (left === null) {
return false
}
if (left.__proto__ === RigPro) {
return true
}
left = left.__proto__
}
}
2.new关键字
new关键字做了什么:
1.在内存中创建一个新对象
2.将这个新对象内部的__proto__
属性赋值为构造函数的prototype属性。
3.构造函数的内部的this被赋值为这个新对象(即this指向新对象)
4.执行构造函数内部的代码(给新对象添加属性)
5.如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
function myNew (func, ...args) {
// myNew函数接收两个参数:构造函数,构造函数所需要的参数
const newObj = {}
newObj.__proto__ = func.prototype
const obj = func.call(newObj, ...args)
if( ( typeof obj == 'object' || typeof obj == 'function' )&& typeof obj !== 'null' ) {
return obj
}
return newObj
}
function Person(name, age) {
this.name = name
this.age = age
}
const p1 = myNew(Person, 'lili', 18)
console.log(p1);
3.call函数
Object.prototype.myCall = function (context, ...args) {
// 当context为null或undefined时,将context赋值为window
context = context || window
// 新建一个fn属性,用来保存当前调用这个函数的this
context.fn = this
const result = context.fn(...args)
delete context.fn
return result
}
const p1 = {
add: function add (a, b) {
return a + b
}
}
console.log(p1.add.myCall(p1, 1, 2));