基础(一)
最近发现JS的基础原理其实还是蛮重要的,有些知识点没有深入了解,就会导致实际项目中的代码做了很多“无用功”。现在开始整理下最近回顾的一些基础的知识:
#### 提升 ##### 声明提升 ``` console.log(r); var r = 'hello world'; // 输出undefined ``` 上述代码等价于 ``` var r; console.log(r); r = 'hello world'; ``` 之所以会输出 undefined,原因就在于声明的提升,var 变量会提升到顶部进行声明。
再举个例子
var r = 'hello world';
var r;
console.log(r);
这段代码输出的会是 hello world 上述代码等价于
var r;
var r;
r = 'hello world';
console.log(r);
函数提升
如下例子
console.log(a);
function a() {};
var a = 1;
输出将会是 f a() {}; 上述代码等价于
function a() {};
var a;
console.log(a);
a = 1;
函数会被提升,且优先于变量提升。
根据提升的例子,说明了var声明的变量会提升到作用域的顶部,且他会被挂在到window 上,这可能就会引发一些不可预估的问题。 下面看看 let 和 const
let a = 1;
var b = 1;
const c = 1;
console.log(window.a);
console.log(window.c);
function t() {
console.log(b);
let b;
}
t();
通过例子发现 a 和 c 都不会被挂载到window 上,而且使用let和const 的时候 如果前面已经声明过变量了,就不可以再次声明,否则会报错。
提升这个概念解决的问题其实是函数间的相互调用:
function test1() {
test2();
}
function test2() {
test1();
}
test1();
如果不存在提升,上述代码执行不了
数组
map
生成一个新数组,遍历原数组,将每个元素拿出来做一些变换然后放入到新数组中。
[1, 2, 3].map(v => v + 1) // [2, 3, 4]
map回调函数接受三个参数,当前索引元素,索引,原数组
fliter
生成一个新数组,在遍历数组的时候将返回值作为true的元素放入新数组,删除一些不必要的元素
[1 , 2, 3].filter(item => item !== 2) // [1, 3]
reduce
将数组中的元素通过回调函数最终转换为一个值
数组元素累加
const a = [1, 2, 3, 4, 5];
const total = a.reduce((acc, currentValue) => acc + currentValue, 0);
reduce 接受两个参数分别是回调函数和初始值。
- 上述代码首先将 0 作为初始值,会传入回调函数中的第一个参数。
- 回调函数接受四个参数,分别是累计值,当前元素,当前索引,原数组。
通过reduce实现map
const a = [1, 2, 3];
a.map(item => item + 1) // [2, 3, 4]
a.reduce((acc, item) => { acc.push(item + 1); return acc; }, [])
slice
不改变原数组 用于返回一个新的指定范围的数组 第一个参数为startIndex,第二个参数为endIndex(可选) 如果第一个参数为负值,从最后一个开始算,-1为倒数第一个 只有第一个参数: 会返回[arr[startIndex], ...] 两个参数都包含的时候: 会返回[arr[startIndex], ..., arr[endIndex - 1]]
const a = [1, 2, 3, 4];
console.log(a.slice(2, 4)) // [3, 4]
splice
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
该方法会改变原始数组
arr.splice(index, howmany, item1, ..., itemx)
index为必须,整数,规定添加/删除item的位置
howmany 必须 要删除的数目
imte1, ..., itemX 可选 向数组添加的新项目
返回一个新数组
apply bind call
apply有两个参数:指向对象,[参数列表] call有两个参数:指向对象,参数列表 bind支持上述两种 但是会返回一个函数 不会马上执行
手写call、apply、bind call
Function.prototype.newCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window;
context.fn = this;
const args = [...arguments].slice(1);
const result = context.fn(...args);
delete context.fn;
return result;
}
- 首先context为可选参数,如果不传入,默认windows
- 判断当前是否为函数
- 给context创建一个fn属性,将值设置为需要调用的函数
- call可传入多个,剥离出来。
- 调用函数后,将对向上的fn删除
apply
Function.prototype.newApply = function(context) {
if (typeof this !== 'function'){
throw new TypeError('Error');
}
context = context || window;
context.fn = this;
let result;
if ([arguments][1]) {
result = context.fn(...args)
} else {
retule = context.fn();
}
delete context.fn;
return result;
}
new的原理是什么 new创建和字面量创建的区别
调用 new 的时候发生以上四件事情:
- 新生成一个空对象
- 链接到原型
- 绑定this
- 返回新的对象
function create() {
let obj = {};
}
instanceof 的原理
function myInstanceof(left, right) {
let prototype = right.prototype;
left = left.__proto__;
whilt(true) {
if (prototype === left) {
return true;
}
if(left === null || left === undefined)
return false;
left = left.__proto__;
}
}
- 首先获取类型的原型
- 然后获取对象的原型
- 然后一直循环判断对象的原型是否等于类型的原型,一直到原型对象为null
为什么0.1 + 0.2 !== 0.3
JS采用IEEE 754双精度版本