Symbol出现的原因/作用
①作为属性 避免属性冲突重复,就是使用它来表示唯一值;
问题是我们什么情况下 要保障属性一定不冲突重复 覆盖呢?
在vue里 有this.$ parent ,this.$ options ,this.$ set 这些,使用$命名开头就是想通过命名约定来减少重复覆盖,
可是通过命名约定没有强制执行,还是存在被新生低级程序员覆盖的可能
在没有Symbol之前 采用 Object.freeze 冻结一个对象 也可以做到 一定不会覆盖修改属性的问题,但是它带来的问题就是失去了灵活性,只想一个属性不被覆盖重复,结果把整个对象都冻结了,这得不偿失。
还要Object.defineProperty(obj,‘a’,{ writable:false }) 这样通过屏蔽一个属性的可写性 来达到不能覆盖一个属性的目的,不好的地方就是麻烦。用symbol最简单。
** 作用一:防止变量名冲突,挂在window下的全局方法属性对象等,还有全局状态等,有很多地方需要保障唯一性。**
//订单已付款
if(orderStatus==='payed'){
//就干点啥
}
//订单已经取消 就干点啥
if(orderStatus==='cancel'){
//就干点啥
}
这个写法存在的问题是什么呢
1.payed cancel字符串可能在多个地方使用时 书写时出现错误;
2.字符串可能会需要修改,认为它表意不准确
3.别人难以一看就知道 orderStatus到底有哪些状态
改成如下:
const OrderStatus={
payed:Symbol('payed'),
cancel:Symbol('cancel')
}
//订单已付款
if(orderStatus===OrderStatus.payed){
//就干点啥
}
//订单已经取消 就干点啥
if(orderStatus===OrderStatus.cancel){
//就干点啥
}
** 作用二:替代字符串 ,规范代码,减少错误。**
BigInt的作用和使用
作用:
BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
使用:
let max = Number.MAX_SAFE_INTEGER;//Number的最大安全整数
console.log(max);
console.log(max + 1);
//超过number的最大数值范围,运算就会出错
console.log(max + 2);
console.log(BigInt(max));
//BigInt数据类型不能直接和普通数据类型进行运算
console.log(BigInt(max) + BigInt(1));
console.log((BigInt(max) + BigInt(2)));
console.log((BigInt(max) + BigInt(2)).toString());
bigint后面都有一个n,区分Number类型,bigint上可以调用tostring方法转为字符串
现在typeof symbol和typeof bigint返回symbol和bigint
对堆和栈的理解
1.stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小也不一定会自动释放
2.引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象
3.基本数据类型存栈,引用数据类型存堆
instanceof
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
可以看到,instanceof只能正确判断引用数据类型,而不能判断基本数据类型。instanceof 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
constructor判断类型
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
constructor有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了:
这里刨析下(2).constructor === Number)或
var str1 = "oujm";
var str2 = str1.substring(2);
一个基本类型不应该有constructor和substring这些属性和方法,那为什么我们可以调用这些呢?
我们忽略了,在JS的世界中有一种对象类型叫包装对象。
实际上,每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型的对象。
再来看上面那个?,str1 很明显是一个基本类型实例,问题就出在 str1.substring(2) 字符串怎么会有方法。其实,为了让我们更好的操作基本类型的实例对象,后台进行了一系列的操作:
创建String的实例
在实例上调用指定的方法
销毁这个实例
从这里能够看到,一般的引用类型和包装类型唯一的区别就在于对象的生命周期。包装类型的对象生命周期很短,只有代码执行的一瞬间,然后就被销毁了,所以这也就是为什么我们不能在运行的时候为基本类型的值添加属性和方法。
这也解答了我曾经的一个疑问
var str1 = "oujm";
var str2 = new String("ethan");
console.log(str1.__proto__ === str2.__proto__); // true
console.log(str1 instanceof String); // false
console.log(str2 instanceof String); // true
同样的道理,在调用__proto__ 属性的瞬间,也是使用new String() 先来实例化一个对象,所以那一瞬间他们的构造函数以及原型对象是相同的,但也仅仅是那一瞬间。
Object.prototype.toString.call()和直接调用toString的区别
这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。
判断数组的方法
Object.prototype.toString.call()
obj.proto === Array.prototype;同instanceof
通过ES6的Array.isArray()做判断
a.constructor===Array
typeof null 的结果为什么是Object?
在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:
000: object - 当前存储的数据指向一个对象。
1: int - 当前存储的数据是一个 31 位的有符号整数。
010: double - 当前存储的数据指向一个双精度的浮点数。
100: string - 当前存储的数据指向一个字符串。
110: boolean - 当前存储的数据是布尔值。
如果最低位是 1,则类型标签标志位的长度只有一位;如果最低位是 0,则类型标签标志位的长度占三位,为存储其他四种数据类型提供了额外两个 bit 的长度。
有两种特殊数据类型:
undefined的值是 (-2)30(一个超出整数范围的数字);
null 的值是机器码 NULL 指针(null 指针的值全是 0)
那也就是说null的类型标签也是000,和Object的类型标签一样,所以会被判定为Object。
valueOf作用
返回给定对象的原始值
也可以使用valueOf方法将包装类型倒转成基本类型:
var a = 'abc'
var b = Object(a)
var c = b.valueOf() // 'abc'
箭头函数继承来的this指向永远不会改变,call()、apply()、bind()等方法不能改变箭头函数中this的指向,箭头函数没有prototype
var id = 'GLOBAL';
Object.prototype.id='111'
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
new obj.a() // 111
new obj.b() // Uncaught TypeError: obj.b is not a constructor
new操作最后一步注意
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
判断return的result逻辑
result && (typeof result === "object" || typeof result === "function");