前置知识
函数基础知识:普通函数调用时,this指向的是调用函数的对象,但是到底是谁调用了函数呢
let name = "let的name";
const person = {
name:"person的name",
getName(){
return this.name;
}
}
const getName = person.getName;
console.log(person.getName());
console.log(getName());
console.log((person.getName)());
console.log((0,person.getName)());
console.log(person.getName())
这里是person调用的getName所以this指向person,打印"person的name"
console.log(getName())
这里调用getName的是window,window上没有name所以返回undefined,外面是let定义的所以不在window上,使用var定义才会被打印
console.log((person.getName)())
这里就看(person.getName)这里面指向是谁,主要看()运算符会不会计算值并赋值给谁
引用的概念:内部引用数据类型不是语言数据类型;用于解释诸如delete、typeof和赋值等操作符的行为;例如,赋值的左操作数应该产生一个引用记录
var num = 10;这里的num可以说是一个标识符也可以说是一个引用
10=10是不能赋值成功的,因为左边的10不是一个引用
赋值操作其实是一个取值和赋值的过程
GetValue(v):取值操作,返回的是确定的值
PutValue(v,w):设置值,对某个引用设置,要求v一定是一个引用
理解v=v
可以理解为 v=GetValue(v)
v作为左手端的时候它是引用,在右边的时候它是值
一些赋值操作
分组运算符:()里面可以是表达式也可以是字面量的值,此算法不将GetValue应用于Expression(表达式)的结果,因为像delete和typeof等操作符可以应用于括号表达式,意思就是分组运算符不计算值
所以(person.getName)没有产生getValue操作,没有发生取值就没有赋值,所以this还是指向的是person
console.log((0,person.getName)()) 逗号运算符,它会对每个操作数求值,并返回从左到右的最后一个操作数的值;所以this指向window
typeof 在非严格模式下不报错,是因为引用不可达就返回undefined
var num = 10;
function evalCode(){
eval(`var num = 20`)
}
function evalCode2() {
(0,eval)(`var num = 30`)
}
console.log(num) // 10
evalCode()
console.log(num)// 10
evalCode2()
console.log(num)// 30
第一个是10,没有疑问
第二个是10,因为执行函数,num变量是在函数内部,不在window,所以num还是10
第三个是30,应该是逗号运算符会计算所以返回值了
delete到底在删除什么
delete的返回值是Boolean,true不一定删除成功,只是删除没有发生异常,false表示一定没有删除成功;delete删除原型上的属性,delete不会去遍历原型链,可以使用delete Foo.prototype.bar删除
delete不能删除的属性
1、任何var声明的属性不能从全局作用域或者函数作用域删除
2、任何let和const声明的属性不能从其作用域删除
3、不可配置的(configurable)属性不能被删除
4、undefined在window上是不可配置的,所以也不能从window上删除
本质:操作表达式的结果
1、对于字面量、值,不操作直接返回true
2、对于引用不可达的也是直接返回true
3、引用类型,删除引用,比如删除数组的某个下标,该下标的值会变成空
严格模式下报错类型
SyntaxError:语法错误,删除变量、函数名、函数参数时报错
TypeError:删除不可配置的属性
ReferenceError:引用错误
console.log(delete null) // true,null是常量,返回true
console.log(delete 11) // true,11是常量直接返回true
console.log(delete undefined) // false,undefined是window上的属性,且不可配置,返回false
a={c:12}
console.log(delete a) // true,不使用var声明的可配置
var b=12;
console.log(delete b) // false,使用var声明的变量不可配置
console.log(delete xxxxx) // true,不可达直接返回true
console.log(delete ({}).toString) // true,没有错误但是没有真正删除
位运算符
操作数必须是32位的整数,如果不是自动转为整数,速度很快
按位与(&)
两个操作数为1则为1,否则为0
用处: 判断奇偶数
奇数:num & 1 === 1
偶数:num & 1 === 0
按位或(|)
一个操作数为1则为1
用处: 取整
x | 0;做位运算必须是整数,再|0得到的是它本身;
x|x ===x
按位非(~)
反转操作数的位,表象是对数字求负,然后减1
用处:
1、判断数组中是否包含某个元素,不存在indexOf返回-1,~-1 = -(-1)-1=0
function inArrayCompare(arr,num) {
if(~arr.indexOf(num)){
return true;
}
return false;
}
2、取整
~~x:-(-x-1)-1=x
按位异或(^)
只当一个数位是1返回1,否则返回0
特点:
1、归零律:a^a=0;自己异或自己,相同位数上的值肯定相同,所以为0
2、恒等律:a^0=a;自己异或0,自己是啥结果是啥
3、自反律:出现奇数次是自己,出现偶数次是0;a^a^a = 0^a = a
4、结合律:a^b^c === c^b^a;和顺序无关
例子: 变量值为数字,完成值的交换不添加临时变量
let a = 10;
let b = 20;
a^=b;
b^=a;
a^=b;
总结
RGB和16进制颜色转换
rgb转16进制
function rgbTohex(rgb) {
// split的参数可以是正则
const rgbArr = rgb.split(/[^\d]+/);
const color = rgbArr[1]<<16 | rgbArr[2]<<8 | rgbArr[3];
return "#"+color.toString(16)
}
16进制转rgb
function hexToRgb(hex) {
let newHex = hex.replace("#","0x"),
r=newHex >> 16,
g=newHex >> 8 & 0xff, // & 0xff(255),用于丢高位
b=newHex & 0xff;
return `rgb(${r},${g},${b})`
}
浮点数运算
进制转换
十进制转二进制
整数部分:除2取余,逆序排列
小数部分:乘二取整,顺序排列
以9.375为例,结果为1001.011
整数部分:1001
小数部分:011
console.log((9.375).toString(2)) // 转换为二进制确实是1001.011
二进制转十进制
整数部分:从右往左,用二进制的每个数乘2的相应次方递增
小数部分:从左往右,用二进制的每个数乘2的相应负次方递减
二进制浮点数算术标准
双精度64,类似科学计数法
符号位S:1表示负数,0表示正数
指数位E:E=指数+1023(偏移码)
尾数部分M:1<=M<2,隐含的以1开头,默认存储1后面的部分
浮点数运算
1、对阶: 保证指数位数字一致
123.5+1426.00456 => 1.235102 + 1.42600456103;这里的阶指的是指数部分,分别是2和3,按照小阶对大阶的规则往3靠拢,变成0.1235103 + 1.42600456103
精度可能丢失,小阶对大阶会右移导致精度丢失;相加或者相减值可能会溢出
2、尾数运算: 尾数进行相加减,1.54950456
3、规格化: 尾数相加减可能会进位,所以需要再次规格化;右移动就会丢失精度
4、舍人处理: 如果右移超出,计算机会保留,到舍入处理的时候会再拿出来进行舍入处理
5、溢出判断: 是否超过最大整数
0.1+0.2
0.1转二进制
规格化:0.000110011(0011)n => 1.10011001(1001)n2-4
符号位S:0正数
指数E:1023 - 4 = 1019 (01111111011)
尾数M:1进0舍
然后经过之前的一系列运算得到规格化的数,然后再转为十进制
正确计算0.1+.02
1、将数字转换成没有小数位进行计算,然后再变成小数 0.110+0.210=3/10
2、使用toFixed(num):四舍五入
3、判断是否存在精度丢失:判断0.1+0.2-0.3是否小于Number.EPSILON