记录一些有意思的问题

字面量(数组字面量,对象字面量,字符串字面量,函数字面量)

我们都知道在JS中,除了六种基本数据类型,其余全部都是属于对象,那么我们创建一个对象的过程如下

1
2
3
4
5
var obj=new Object()
obj.a='aaa';
obj.b='bbb'
obj.c='ccc'
obj.c//"ccc"

但是我们大部分情况下不用这种方式创建对象,我们用这个方式

1
2
3
4
5
6
var obj = {
a:'aaa',//a是属性,'aaa'是属性值
b:'bbb',
c:'ccc'
}
obj.c//"ccc"

我们在创建数组,对象,字符串,函数的时候不需要用new操作符

它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的,(每个实例对象没有自己的特性)

内置对象

内置对象

关联数组

1
2
3
4
5
6
7
var arr=new Array();
arr["china"]="beijing,niaoling,hulan";
arr["usa"]="newyork,washington,atlanta";
arr["japan"]="tokyo";
alert(arr["china"]);
alert(arr["japan"]);
alert(arr[0]);

注意上面的alert(arr[0]);这一句,它会返回undifined。这就意味着,关联数组中,不能再以传统的下标方式来访问数组元素而必须通过元素的名字。

这种通过名字来访问数组元素的形式有可读性高,灵活方便的优势。

apply,bind的黑魔法

如果一个数组我们已知里面全都是数字,想要知道最大的那个数,由于Array没有max方法,Math对象上有

我们可以根据apply传递参数的特性将这个数组当成参数传入

最终Math.max函数调用的时候会将apply的数组里面的参数一个一个传入,恰好符合Math.max的参数传递方式

这样变相的实现了数组的max方法。min方法也同理

1
2
3
const arr = [1,2,3,4,5,6]
const max = Math.max.apply(null, arr)
console.log(max) // 6

如果你想将某个函数绑定新的this指向并且固定先传入几个变量可以在绑定的时候就传入,之后调用新函数传入的参数都会排在之后

1
2
3
4
const obj = {}
function test(...args) {console.log(args)}
const newFn = test.bind(obj, '静态参数1', '静态参数2')
newFn('动态参数3', '动态参数4')

当我们使用一个函数需要改变this指向的时候才会用到call apply bind

可以定义一个没有单位的值作为缩放因子来统一控制行高,缩放因子是直接继承的,而不是继承计算值

1
2
3
4
5
6
<div style="background:#ddd;height:100px;line-height:1.8;font-size:15px;">
<p style="font-size: 30px;">
中文 English<br/>
中文 English
</p>
</div>

如果line-height有em或者百分比单位

1
2
3
4
5
6
<div style="background:#ddd;height:100px;line-height:180%;font-size:15px;">
<p style="font-size: 30px">
中文 English<br/>
中文 English
</p>
</div>

这里div的行高为180%*15px=27px

而p的行高为计算后的27px 而非180%*30px=54px

通过上面的例子我们知道文本之间的空白距离不仅仅是行高决定的,同时也受字号的影响

如果继承的是计算值,那么当元素内的文字字体尺寸不一样的时候,就有可能造成字体的重叠

并发和并行

并发是宏观概念,我分别有任务 A 和任务 B,在一段时间内通过任务间的切换完成了这两个任务,这种情况就可以称之为并发

并行是微观概念,假设 CPU 中存在两个核心,那么我就可以同时完成任务 A、B。同时完成多个任务的情况就可以称之为并行

原始数据类型的bug

六种原始数据类型:boolean,null,undefined,number,string,symbol,存储的都是值,是没有函数可以调用的,比如undefined.toString()

此时你肯定会有疑问,这不对呀,明明 '1'.toString() 是可以使用的。其实在这种情况下,’1’ 已经不是原始类型了,而是被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数

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

get 和 post

GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同

GET产生一个TCP数据包;POST产生两个TCP数据包。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)

[] == ![] 结果为true

[] == ![] 的判断本质上是原始类型和引用类型的判断,一元运算符 ! 优先级高于二元运算符 ==,实际 == 判断时表达式已经转为 [] == false,然后 == 导致的隐式转换,二者都转为数值 0 == 0 就返回 true 了

如何解决async await遇到reject停止执行下面语句

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数

对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的

null:所有机器码均为0

undefined:用 −2^30 整数来表示

所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待

去除数组的重复成员

[…new Set(array)]

@click.native

给vue组件绑定事件时候,必须加上native ,不然不会生效(监听根元素的原生事件,使用 .native 修饰符)

等同于在自组件中:子组件内部处理click事件然后向外发送click事件:$emit(“click”.fn)

连续声明怎么回事

var v1,v2,v3=’hello’ 这行代码中,声明了 v1,v2 但是没有赋值,声明了 v3 并且赋值了

var v4=v5=v6=’hello’; 这行代码的意思是,首先,给全局变量v6赋值,v6=’hello’,因为没有声明v6,所以自动创建一个全局变量。然后在把v6的值赋值给v5(和v6一样的意思)。最后声明变量v4(这个是声明变量并且赋值),在赋值

非基础数据类型的赋值问题

无声明语句的变量赋值,直接是全局的变量,挂在在window中

object变量赋值如 const a = [1,2,3] const b = a

是地址的赋值 改变其中一个另一个也会改变,但是如果这样a = [1,2]

这是重新分配了地址,a和b就毫无相关了

[] == true

if([])if([] == true)是不等价的if([])的含义:[]是否为“真值”if([] == true)的含义:[]与true是否相等。(如果使用===,表示绝对相等,需要类型相同并且值相同;如果使用==,则在类型不同的情况下会进行类型转换,然后再比较)js里的“真值”很好判断,因为“假值”总共只有6个:false,undefined,null,0,””(空字符串),NaN除此之外的所有值,都是“真值”,即在逻辑判断中可以当true来使用但是这些“真值”并不一定等于true,因为比较的时候发生了类型转换,此处比较会将操作数转换为数值类型。

new命令的作用,就是执行一个构造函数,并且返回一个对象实例

使用new命令时,它后面的函数调用就不是正常的调用,而是依次执行下面的步骤。

a:创建一个空对象,作为将要返回的对象实例。

b:将空对象的原型指向了构造函数的prototype属性。

c:将空对象赋值给构造函数内部的this关键字。

d:开始执行构造函数内部的代码。

for循环的特别之处

就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

1
2
3
4
5
6
7
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

new命令的基本原理

a:创建一个空对象,作为将要返回的对象实例。

b:将空对象的原型指向了构造函数的prototype属性。

c:将空对象赋值给构造函数内部的this关键字。

d:开始执行构造函数内部的代码。

也就是说,构造函数内部,this指向的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所谓构造函数,意思是这个函数的目的就是操作一个空对象(即this对象),将其构造为需要的样子。

出现要修改的bug时候的git操作

修改bug的时候git的操作方法 先git stash保存工作台修改内容 然后git branch x新建x分支 在x分支上修改bug 改完push玩到master分支merge这个x 然后回到自己分支 git stash pop恢复工作台 自己分支时不时的merge master。修改完bug的时候直接删除x分支

函数柯里化

函数柯里化是函数式编程(此函数为数学函数)的一种运算方式,阮一峰解释说是把一个多参数函数转化为单参数函数。

在 weex 中将 weex 相关的 nodeOps 和 modules 传入给 path 函数,在 web 中将 web 相关的 nodeOps 和 modules 传入给 path 函数,它们都调用了同一个 createpatchFunction 函数,这个函数将传入的差异化参数进行处理,最终返回一个无差异参数的 path 函数,那么之后再调用这个 path 函数时,就不需要处理一大堆 if else 逻辑。

所以函数柯里化的基本用法是将多参数或者差异化参数转变成单参数或者无差异化参数的一种函数式编程的运算方式。

箭头函数指向

箭头函数的特点之一就是不绑定新的this,所以箭头函数的this是在词法层面就绑定到了外层作用域,他的this只能是来自外层作用域的this,无论你通过什么方式都不能改变,除非你修改了外层作用域的this。

运行时 + 编译器 vs. 只包含运行时

0%