目录
3、Object.prototype.toString.call()
五、Object.prototype.toString.call()与Array.prototype.toString.call()的区别
十六、JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点
一、JS的数据类型都有哪些
1、基本数据类型:
- Number
- String
- Boolean
- null
- Undefined
- Symbol
- BigInt
2、引用数据类型
统称为Object类型,具体有:
- RegExp
- Date
- Function
- Array
- Object
二、数据类型的存储方式
- 基本数据类型:存储在栈中
- 引用数据类型:存储在堆中
- 两种方式的联系:在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。
- 两种方式的区别:栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗
三、为什么0.1+0.2>0.3
因为在JS底层中,每个变量是以二进制表示,固定长度为64位,其中第1位是符号位,再往后11位是指数为,最后52表示的是尾数位,而0.1和0.2转为二进制的时候是无限循环小数,所以JS就会进行截取,截取以后0.1和0.2就不是他们本身了,要比原来大那么一丢丢,所以0.1+0.2就>0.3了
解决办法:先给他们放大倍数,随后在除以相应倍数
const a = 0.1;
const b = 0.2;
console.log(a + b === 0.3) // false
console.log((a * 1000 + b * 1000) / 1000 === 0.3) // true
四、数据类型的判断方式
1、typeof
特点:无法分辨是null
还是Object
typeof undefined // 'undefined'
typeof '10' // 'String'
typeof 10 // 'Number'
typeof false // 'Boolean'
typeof Symbol() // 'Symbol'
typeof Function // ‘function'
typeof null // ‘Object’
typeof [] // 'Object'
typeof {} // 'Object'
2、instanceof
特点:只能判断某对象是否存在于目标对象得的原型链上,不能判断字面量的基本数据类型
function Foo() { }
var f1 = new Foo();
var d = new Number(1)
console.log(f1 instanceof Foo);// true
console.log(d instanceof Number); //true
console.log(123 instanceof Number); //false -->不能判断字面量的基本数据类型
3、Object.prototype.toString.call()
特点:可以分清各种数据类型,但是不能细分为谁谁的实例
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call("abc")); // "[object String]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(Object.prototype.toString.call(fn));// "[object Function]"
console.log(Object.prototype.toString.call(date));// "[object Date]"
console.log(Object.prototype.toString.call(arr)); // "[object Array]"
console.log(Object.prototype.toString.call(reg));// "[object RegExp]"
4、constructor
var d = new Number(1)
var e = 1
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(e.constructor);//ƒ Number() { [native code] }
console.log(e.constructor.name);//Number
console.log(fn.constructor.name) // Function
console.log(date.constructor.name)// Date
console.log(arr.constructor.name) // Array
console.log(reg.constructor.name) // RegExp
五、Object.prototype.toString.call()与Array.prototype.toString.call()的区别
Object.prototype.toString.call()
返回的是统一格式Array.prototype.toString.call()
的部分类型无法检验,无法检验null、undefined,会报错,数组也不会输出其是Array类型
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(Array.prototype.toString.call(undefined)); // 报错
console.log(Array.prototype.toString.call(null)); // 报错
console.log(Array.prototype.toString.call(123)); // "[object Number]"
console.log(Array.prototype.toString.call("abc")); // "[object String]"
console.log(Array.prototype.toString.call(true)); // "[object Boolean]"
console.log(Array.prototype.toString.call(fn)); // "[object Function]"
console.log(Array.prototype.toString.call(date)); // "[object Date]"
console.log(Array.prototype.toString.call(arr)); // "1,2,3"
console.log(Array.prototype.toString.call(reg));// "[object RegExp]"
六、为什么typeof null 是Object
因为在JavaScript中,不同的对象都是使用二进制存储的,如果二进制前三位都是0的话,系统会判断为是Object类型,而null的二进制全是0,自然也就判断为Object
【拓展】000 =》对象;1 =》整型;010 =》双精度类型;100 =》字符串;110 =》布尔类型
七、==
和===
有什么区别
1、===
严格意义上的相等,会比较两边的数据类型和值大小
- 数据类型不同返回false
- 数据类型相同,但值大小不同,返回false
2、==
非严格意义上的相等,
-
两边类型相同,比较大小
-
两边类型不同,根据下方表格,再进一步进行比较。
Null == Undefined ->true
String == Number ->先将String转为Number,在比较大小
Boolean == Number ->现将Boolean转为Number,在进行比较
Object == String,Number,Symbol -> Object 转化为原始类型
八、NaN === NaN
返回什么
返回 false
,NaN
永远不等于NaN。
判断是否为NaN
用一个函数isNaN
来判断。isNaN
传入的如果是其他数据类型,那么现将它使用Number()
转为数字类型在进行判断
九、字面量创建对象和new创建对象有什么区别
字面量:
- 字面量创建对象更简单,方便阅读
- 不需要作用域解析,速度更快
new:
- 创建一个新对象
- 使新对象的__proto__指向原函数的prototype
- 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
- 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result
十、new会发生的事
- 创建一个新对象
- 使新对象的__proto__指向原函数的prototype
- 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
- 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result
手写new:见=》JavaScript手写题_Kw_Chng的博客-CSDN博客
十一、call、apply、bind的区别
1、使用
fn.call(thisArg, arg1, arg2, arg3, ...)
调用fn.call时会将fn中的this指向修改为传入的第一个参数thisArg;
将后面的参数传入给fn,并立即执行函数fn
apply(thisArg, [argsArr])
fn.apply的作用和call相同:修改this指向,并立即执行fn。
apply接受两个参数,第一个参数是要指向的this对象,
第二个参数是一个数组,数组里面的元素会被展开传入fn,作为fn的参数
bind(thisArg, arg1, arg2, arg3, ...)
fn.bind的作用是只修改this指向,但不会立即执行fn;会返回一个修改了this指向后的fn。
需要调用才会执行:bind(thisArg, arg1, arg2, arg3, ...)()。bind的传参和call相同
2、相同点
- 三个都是用于改变this指向;
- 接收的第一个参数都是this要指向的对象;
- 都可以利用后续参数传参。
3、不同点
- call和bind传参相同,多个参数依次传入的;
- apply只有两个参数,第二个参数为数组;
- call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数。
手写call、apply、bind:见=》JavaScript手写题_Kw_Chng的博客-CSDN博客
十二、什么是作用域,什么是作用域链
- 规定变量和函数的可使用范围称作作用域
- 每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链
十三、什么是执行栈,什么是执行上下文
1、执行上下文
1.1、全局执行上下文
创建一个全局的window对象,并规定this指向window,执行js的时候就压入栈底,关闭浏览器的时候才弹出
1.2、函数执行上下文
每次函数调用时,都会新创建一个函数执行上下文
执行上下文分为创建阶段和执行阶段
- 创建阶段:函数环境会创建变量对象:arguments对象(并赋值)、函数声明(并赋值)、变量声明(不赋值),函数表达式声明(不赋值);会确定this指向;会确定作用域
- 执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象
1.3、eval执行上下文
2、执行栈
- 首先栈特点:先进后出
- 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈,当程序执行完成时,它的执行上下文就会被销毁,进行弹栈。
- 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文
- 只有浏览器关闭的时候全局执行上下文才会弹出
十四、什么是闭包?闭包的作用?闭包的应用?
1、是什么
一个函数和对其周围状态(词法环境)的引用捆绑在一起, 这样的组合就是闭包。闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
2、特点,作用
- 让外部访问函数内部变量成为可能;
- 可以避免使用全局变量,防止全局变量污染;
- 可以让局部变量常驻在内存中;
- 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
3、应用
- 设计模式中的单例模式
- for循环中的保留i的操作
- 防抖和节流
- 函数柯里化
十五、原型和原型链
1、原型
2、原型链
读取对象属性时,会优先对象自身属性,如果对象中有,则使用,没有则去对象的原型中寻找如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null))。如果依然没有找到,则返回undefined。
十六、JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点
1、原型继承
- 把父类的实例作为子类的原型
- 子类的实例共享了父类构造函数的引用属性
- 不能传参
var person = {
friends: ["a", "b", "c", "d"]
}
var p1 = Object.create(person)
p1.friends.push("aaa")
console.log(p1);
console.log(person);
2、组合继承
- 在子函数中运行父函数,但是要利用call把this改变一下,再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor
- 优点可传参,不共享父类引用属性
- 调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用
3、寄生组合继承
function Father(name) {
this.name = name
this.hobby = ["篮球", "足球", "乒乓球"]
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son
var s2 = new Son("ming", 18)
console.log(s2);
4、ES6的extends
当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)。继承发生时,被继承的类称为 父类(超类),继承的类称为 子类。
class Son3 extends Father { // Son.prototype.__proto__ = Father.prototype
constructor(y) {
super(200) // super(200) => Father.call(this,200)
this.y = y
}
}
十七、null和undefined的区别
1、undefined不是关键字,而null是关键字;
2、undefined和null被转换为布尔值的时候,两者都为false;
3、undefined在和null进行==比较时两者相等,===比较时两者不等
4、使用Number()对undefined和null进行类型转换,Number(null)===0,Number(undefined) === NaN
5、undefined本质上是window的一个属性,而null是一个对象;
6、undefined和null的用途 :null表示没有对象,即不应该有值,经常用作函数的参数,或作为原型链的终点。undefined表示缺少值,即应该有值,但是还没有赋予(变量提升时默认会赋值为undefined,函数参数未提供默认为undefined,函数的返回值默认为undefined)