15、展开语法(Spread syntax)
- 函数调用入参展开
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
// expected output: 6
console.log(sum(...numbers));
- 字面量数组构造或字符串,如
[...iterableObj, '4', ...'hello', 6];
构造字面量对象时,进行克隆或者属性拷贝(ECMAScript 2018规范新增特性)。
/*
* 字典对象
*/
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// 克隆后的对象: { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }
/*
* 数组对象
*/
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
// 数组拼接
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];
/*
* 拷贝行为,以数组为例
*/
var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4);
// arr2 此时变成 [1, 2, 3, 4]
// arr 不受影响
14、JavaScript函数
- js函数中有个储存参数的数组
arguments
,所有函数获得的参数会被编译器挨个保存到这个数组中
function sum (){
var a = arguments[0];
var b = arguments[1];
return a+b;
}
// 等价于
function sum (a, b){
return a+b;
}
- 调用该函数的时候,不需要传入所有的参数,它就能正常执行,未入参的参数默认就是
undefined
。
多参数和可选参数
- 永远只能把可选参数连续地声明在函数的末端,必要的参数必须得放前面
function Person(name, age, height, weight) {
...
}
// 这时候age="170cm",调用出错
var person = new Person("Turtle", "170cm");
-
单一的字典参数,变相解决多参数问题:
13、JavaScript的内存机制
- 作用域(scope)
使用var 声明的变量是局部变量,不使用var 直接写变量名是全局变量(更准确的是说,默认被添加到全局window属性中去了)。
/* 例子1 */
var foo = function() {
var local = {}; // 局部变量
};
foo();
console.log(local); //=> undefined
/* 例子2 */
var bar = function() {
local = {}; // 全局变量
};
bar();
console.log(local); //=> {}
12、const、let、var
- const
不可以修改,而且必须初始化,否则会报错。 - var
可以修改,如果不初始化会输出undefined
,不会报错。 - let
第一次接触let关键字,有一个要非常非常要注意的概念就是”javascript 严格模式”
// let遵守 ”javascript 严格模式” ,不加会报错
'use strict';
let hello = 'hello world.';
console.log(hello);
块级作用域:函数内部使用let定义后,对函数外部无影响。
'use strict';
// 例子 1
let c = 3;
console.log('函数外let定义c:' + c);//输出c=3
function change(){
let c = 6;
console.log('函数内let定义c:' + c);//输出c=6
}
change();
console.log('函数调用后let定义c不受函数内部定义影响:' + c);//输出c=3
// 例子 2
(function() {
var varTest = 'test var OK.';
let letTest = 'test let OK.';
{
var varTest = 'varTest changed.';
let letTest = 'letTest changed.';
}
console.log(varTest); //输出"varTest changed.",内部"{}"中声明的varTest变量覆盖外部的letTest声明
console.log(letTest); //输出"test let OK.",内部"{}"中声明的letTest和外部的letTest不是同一个变量
}());
11、判断变量值是否为空?
JavaScript的空值有:undefined、 null、 ''、 NaN、false、0、[]、{} 、空白字符串
。
本身没有判断一个变量值是不是空值的函数,因为变量有可能是string,object,number,boolean等类型,类型不同,判断方法也不同。
function isEmpty(v) {
switch (typeof v) {
case 'undefined':
return true;
case 'string':
if (v.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true;
break;
case 'boolean':
if (!v) return true;
break;
case 'number':
if (0 === v || isNaN(v)) return true;
break;
case 'object':
// 如:数组
if (null === v || v.length === 0) return true;
// 如:字典
for (var i in v) {
return false;
}
return true;
}
return false;
}
10、JavaScript的单例模式
- 通过函数属性构造单例
function Single(){}
Single.getInstance=function(){
if(!this.instance){
this.instance=new Single();
}
return this.instance
};
// 实例化
let a=Single.getInstance();
let b=Single.getInstance();
console.log(a===b);//true
9、Object.assign()
-
Object.assign(target, ...sources)
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象;它将返回目标对象。
对于target和sources之间相同的属性是直接覆盖的,如果属性值为对象,是不会对对象直接的属性进行合并的。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
-
Object.assign()
到底是深拷贝还是浅拷贝?
如果对象的属性值为简单类型,如string、number等,通过Object.assign(...);得到的新对象为深拷贝;如果属性值为对象或其他引用类型,那对于这个对象而言其实是浅拷贝的。
8、JavaScript中的this和self之间的区别
https://www.itranslater.com/qa/details/2325926566821364736
this
每个函数在定义被ECMAScript解析器解析时,都会创建两个特殊的变量:this和arguments。这个this对象是在运行时基于函数的执行环境绑定的。
因此,如果在在全局对象中,this指向的是window对象;在自定义函数中,this对象指向的是调用这个函数的对象。self
浏览器会首先创建一个窗口,这个窗口就是一个window对象,也是js运行所依附的全局环境对象和全局作用域对象。self 指窗口本身,它返回的对象跟window对象是一模一样的。
7、数组遍历方法和效率
6、TypeScript(为狂奔的JS套上规矩)
6.1 ES6
5、Promise原理
- callbacks队列:保存状态变化的回调方法。
- 状态机制:引入pending --> fulfilled/rejected三个状态及其变化。
- 异步机制,利用setTimeout延迟异步执行。
- 链式机制:在所有方法返回Promise,即可实现链式操作。
4、异步处理,Promise(ES6)
有且只有只有三种状态:pending --> fulfilled/rejected(成功/失败)
这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。
var promise = new Promise(function(resolve, reject) {
// 异步处理
if (success) {
resolve();
} else {
reject();
}
});
.then
方法
1、可以接收构造函数中处理的状态变化,并分别对应执行。
2、方法有2个参数,第一个函数接收resolved状态的执行,第二个参数接收reject状态的执行,参数可以为空。
3、方法的执行结果会返回一个Promise对象,因此可以进行then的链式执行,这也是解决回调地狱的主要方式。
var fn = function(num) {
return new Promise(function(resolve, reject) {
if (typeof num == 'number') {
resolve(num);
} else {
reject('TypeError');
}
})
}
/*
* 1、then方法的两个参数
*/
fn(1).then((num)=>{
console.log('参数1:数字 ' + num);
}, (num)=>{
console.log('参数2:非数字 ' + num);
});
fn('abc').then(null, (num)=>{
console.log('参数2:非数字 ' + num);
});
/*
* 2、级联
*/
fn(2).then(function(num) {
console.log('first: ' + num);
return num + 1;
})
.then(function(num) {
console.log('second: ' + num);
return num + 1;
})
.then(function(num) {
console.log('third: ' + num);
return num + 1;
});
// 输出结果
first: 2
second: 3
third: 4
.catch
错误捕获
.catch(function(err) {
console.log(err);
})
Promise.all
Promise.all接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。
使用场景:task1、task2并行执行,等待前两个任务都执行完后,流程才能继续,才能执行task3.
Promise.race
只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then
方法了,只会调用一次.then
方法。
使用场景:可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
3、js文件/模块导入和导出
module.exports与exports,export与export default之间的关系和区别
CommonJS模块规范(一切以module
为主,关键字module.exports
和 require
)
根据这个规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
CommonJS规范规定,每个模块内部,module
变量代表当前模块。这个变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。加载某个模块,其实是加载该模块的module.exports
属性。
// 1、example.js 文件
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
// 或者
module.exports = {
x,
addX
}
// 2、导入文件
var example = require('./example.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
exports
和 module.exports
的区别
-
exports
返回的是模块函数,可以直接调用。 -
module.exports
返回的是模块对象本身,返回的是一个类;要new对象之后才可以调用。
ES6模块规范
ES6使用 export
和 import
来导出、导入模块。
// Name Export | Name Import 命名输出
export const name = 'value'
import { name } from '...'
// Default Export | Default Import;默认输出
export default 'value'
import anyName from '...'
// Rename Export | NameImport;重命名输出
export { name as newName }
import { newName } from '...'
// Name + Default | Import All 全部导入
export const name = 'value'
export default 'value'
import * as anyName from '...'
// Export List + Rename | Import List + Rename 列表输出
export {
name1,
name2 as newName2
}
import {
name1 as newName1,
newName2
} from '...'
2、定时器
- 设置定时器
setTimeout(function,delay); //设置延时多少毫秒执行该函数,只执行一次,返回值是一个id
setInterval(function,delay); //设置间隔多少毫米一直执行该函数,执行多次,返回值是一个id
- 取消定时器
clearTimeout(id); //取消由setTimeout方式开启的定时器
clearInterval(id); //取消由setInterval方式开启的定时器
- 原理分析:参见浏览器原理的「JS单线程引擎」
本质就是,JS代码都是单线程执行的,利用 事件机制EventLoop + 任务回调队列 实现异步功能。
1、JSON对象和字符串互转
- 由JSON字符串转换为JSON对象
var obj = str.parseJSON();
var obj = JSON.parse(str);
- 将JSON对象转化为JSON字符串
var str=obj.toJSONString();
var str=JSON.stringify(obj);
- 获取字典所有key和value
Object.keys(dic);
Object.values(dic);
- uni-app 为什么不支持 new XMLHttpRequest() ?
这是h5的专用api。
非h5,不管是小程序还是app都没有window对象,要用uni.request。
0、数组、字典、字符串操作
字符串
- 替换
var text = "Hello World!";
// 替换Hellow为123,g表示全局替换,i表示不区分大小写
var text = text.replace(/Hello/gi, '123');
// 替换变量的值,只能使用正则
var ch = 'Hello';
var text = text.replace(RegExp(ch, 'gi'), '123');
// 转化
parseFloat(str);
parseInt(str)
数组操作
var list = ["haha", 1, 2, "你好"];
list.push("name"); // 添加新元素
list.pop(); // 删除最后一个元素
list.reverse(); // 倒叙
list.slice(1); // 浅拷贝:截取从1位置之后的数组
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1, 3);
list.indexOf('haha') ; // 返回对象数组下标
// 清空数组
var ary = [1,2,3,4];
ary.splice(0,ary.length);//清空数组
console.log(ary); // 输出 [],空数组,即被清空了
浅拷贝 和 深拷贝
- 默认的都是浅拷贝,如直接赋值,arr.slice、arr.concat等方法。
- 深拷贝
1、使用JSON.stringify和JSON.parse
缺点:JSON.stringify()有一些局限,比如对于RegExp类型和Function类型则无法完全满足,而且不支持有循环引用的对象。
var newArr = JSON.parse(JSON.stringify(arr));
2、深拷贝的一个通用方法
实现思路:拷贝的时候判断属性值的类型,如果是对象,继续递归调用深拷贝函数
var deepCopy = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是一个对象
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
// 遍历obj,并且判断是obj的属性才拷贝
if (obj.hasOwnProperty(key)) {
// 判断属性值的类型,如果是对象递归调用深拷贝
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
字典
// 更新和新值
var dic = {};
dic[key] = "name";
// 遍历
for (var key in dic) {
var item = dic[key];
console.log(item);
}
其他
toFixed(1) //保留一位小数点
运算符|| 和 &&
关于宏 和 常量
标准中,没有宏定义的概念:
JavaScript是解释型语言,不需要编译器,因此就没有宏定义的概念(宏定义是在编译器的预处理进行替换的)。标准中,没有枚举的概念。
1、使用字典模拟枚举概念
var Fruit = {
orange : 1,
banana : 2,
peach : 3,
strawberry : 4
};
console.log(Fruit.orange);
2、在TypeScript中支持枚举类型,关键字enum
// 下标从0开始
enum Color {Red, Green, Blue}
- 标准中,没有常量定义。
模拟常量概念