零碎知识点
1、 js for循环 for in for of
for in
这个是常规的 循环
for (var i=0; i<5; i++) {
x=x + "该数字为 " + i + "<br>";
}
for-in: 首先for in最多的是用在了 循环遍历对象的属性:
var person={fname:"Bill",lname:"Gates",age:56};
for (x in person) // x 为属性名 { consol.log(x); } x 输出 fname lname age
但是一定要注意 for in 用在数组里面就是 遍历索引了
var str = ['num1','num2']
for(let s in str){
console.log(s);
}
1 2
for of
Arrays(数组)
Arrays(数组)就是类列表(list-like)对象。数组原型上有各种方法,允许对其进行操作,比如修改和遍历等操作。下面手在一个数组上进行的 for...of 操作:
// array-example.js
const iterable = ['mini', 'mani', 'mo'];
for (const value of iterable) {
console.log(value);
}
// Output:
// mini
// mani
// mo
其结果就是打印出 iterable 数组中的每一个值。
普通对象不可迭代
for...of 循环仅适用于迭代。 而普通对象不可迭代。 我们来看一下:
const obj = { fname: 'foo', lname: 'bar' };
for (const value of obj) { // TypeError: obj[Symbol.iterator] is not a function
console.log(value);
}
在这里,我们定义了一个普通对象 obj ,并且当我们尝试 for...of 对其进行操作时,会报错:TypeError: obj[Symbol.iterator] is not a function。
我们可以通过将类数组(array-like)对象转换为数组来绕过它。该对象将具有一个 length 属性,其元素必须可以被索引。我们来看一个例子:
// object-example.js
const obj = { length: 3, 0: 'foo', 1: 'bar', 2: 'baz' };
const array = Array.from(obj);
for (const value of array) {
console.log(value);
}
// Output:
// foo
// bar
// baz
Array.from() 方法可以让我通过类数组(array-like)或可迭代对象来创建一个新的 Array(数组) 实例
2、splice方法和slice方法
splice()方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]
months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]
[返回值]
由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。
[从第 0 位开始删除 2 个元素,插入"parrot"、"anemone"和"blue"]
var myFish = ['angel', 'clown', 'trumpet', 'sturgeon'];
var removed = myFish.splice(0, 2, 'parrot', 'anemone', 'blue');
// 运算后的 myFish: ["parrot", "anemone", "blue", "trumpet", "sturgeon"]
// 被删除的元素: ["angel", "clown"]
[从第 2 位开始删除 2 个元素]
var myFish = ['parrot', 'anemone', 'blue', 'trumpet', 'sturgeon'];
var removed = myFish.splice(myFish.length - 3, 2);
// 运算后的 myFish: ["parrot", "anemone", "sturgeon"]
// 被删除的元素: ["blue", "trumpet"]
[从倒数第 2 位开始删除 1 个元素]
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(-2, 1);
// 运算后的 myFish: ["angel", "clown", "sturgeon"]
// 被删除的元素: ["mandarin"]
[从第 2 位开始删除所有元素]
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2);
// 运算后的 myFish: ["angel", "clown"]
// 被删除的元素: ["mandarin", "sturgeon"]
- slice() 方法返回一个新的数组对象,这一对象是一个由
begin和end决定的原数组的浅拷贝(包括begin,不包括end)。原始数组不会被改变。 记住这个浅拷贝是只有第一层进行深拷贝,
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
[返回现有数组的一部分]
//这个就说明是 第一层是深拷贝,slice不会改变原有数组
var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
var citrus = fruits.slice(1, 3);
// fruits contains ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']
// citrus contains ['Orange','Lemon']
-
关于slice 是第一层深拷贝的问题
const originArray = [1,2,3,4,5]; const cloneArray = originArray.slice(); console.log(cloneArray === originArray); // false cloneArray.push(6); // [1,2,3,4,5,6] //给拷贝的数组添加,不会改变原始数组,说明是深拷贝 console.log(originArray); [1,2,3,4,5];同样地,我们试试多层的数组。
const originArray = [1,[1,2,3],{a:1}]; const cloneArray = originArray.slice(); console.log(cloneArray === originArray); // false cloneArray[1].push(4); cloneArray[2].a = 2; //可以看到,只有第一层的1是深拷贝,但是对于多层的数组[1,2,3]和对象{a:1} 他只会拷贝引用地址,所以改变克隆之后,原数据里面的 也会改变 console.log(originArray); // [1,[1,2,3,4],{a:2}]
3、join方法
**join()** 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
[语法]
arr.join([separator])
参数
-
separator可选指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。如果缺省该值,数组元素用逗号(
,)分隔。如果separator是空字符串(""),则所有元素之间都没有任何字符。 -
返回值
一个所有数组元素连接的字符串。如果
arr.length为0,则返回空字符串。
[描述]
所有的数组元素被转换成字符串,再用一个分隔符将这些字符串连接起来。
如果一个元素为 undefined 或 null,它会被转换为空字符串。
[示例]
[使用四种不同的分隔符连接数组元素]
下例首先创建了一个数组 a,包含有三个元素,然后用四种不同的分隔符连接所有数组元素。首先是默认的分隔符逗号,然后是一个逗号加空格,接下来是一个加号前后加空格,最后是一个空字符串。
var a = ['Wind', 'Rain', 'Fire'];
var myVar1 = a.join(); // myVar1的值变为"Wind,Rain,Fire"
var myVar2 = a.join(', '); // myVar2的值变为"Wind, Rain, Fire"
var myVar3 = a.join(' + '); // myVar3的值变为"Wind + Rain + Fire"
var myVar4 = a.join(''); // myVar4的值变为"WindRainFire"
[连接类数组对象]
下面的示例将连接类数组对象(arguments),通过在Array.prototype.join上调用Function.prototype.call。
function f(a, b, c) {
var s = Array.prototype.join.call(arguments);
console.log(s); // '1,a,true'
}
f(1, 'a', true);
4、Object.assign()
**Object.assign()** 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
语法
Object.assign(target, ...sources)
-
target目标对象。
-
sources源对象。
目标对象。
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是(可枚举)属性值。
假如源值是一个对象的引用,它仅仅会复制其引用值。
const log = console.log;
function test() {
'use strict';
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
log(JSON.stringify(obj2));
// { a: 0, b: { c: 0}}
obj1.a = 1;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 0}}
log(JSON.stringify(obj2));
// { a: 0, b: { c: 0}}
obj2.a = 2;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 0}}
log(JSON.stringify(obj2));
// { a: 2, b: { c: 0}}
obj2.b.c = 3;
log(JSON.stringify(obj1));
// { a: 1, b: { c: 3}}
log(JSON.stringify(obj2));
// { a: 2, b: { c: 3}}
// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
log(JSON.stringify(obj3));
// { a: 0, b: { c: 0}}
}
test();
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
属性被后续参数中具有相同属性的其他对象覆盖。
const o1 = { a: 1 };
const o2 = { [Symbol('foo')]: 2 };
const obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]
const obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
5、push、unshift
1、push()、pop()和unshift()、shift()
联系:这两组同为对数组的操作,并且会改变数组的本身的长度及内容。
区别:不同的是 push()、pop() 是从数组的尾部进行增减,unshift()、shift() 是从数组的头部进行增减。
var arr = [1, 2];
2、push()和unshift()
向数组的 尾部/头部 *添加若干元素*,并返回 数组的 新长度;返回值是数组的新长度
arr.push(3,4); //返回 arr 的新长度 4
arr ; // arr = [1,2,3,4];
arr.unshift(0,0.5); // 返回 arr 的新长度 6
arr ; // arr = [0,0.5,1,2,3,4];
3、pop()和shift()
从数组的 尾部/头部 *删除1个元素(删且只删除1个)*,并返回 被删除的元素;空数组是继续删除,不报错,但返回undefined;
arr.pop(); //返回 4;
arr ; // arr = [0,0.5,1,2,3];
arr.pop(); //返回 3;
arr ; // arr = [0,0.5,1,2];
arr.shift(); // 返回 0 ;
arr ; // arr = [0.5,1,2]
PS: pop()和shift() 不接受传参,即使传了参数也没什么卵用~~;
arr.pop(3) ; // 返回 2;永远返回最后一个;
arr ; // arr = [0.5,1];
arr.shift(1); // 返回 0.5; 永远返回第一个;
arr ; // arr = [1];
arr.pop() ; // 返回 1;
arr ; // arr = [];
arr.shift() // 返回 undefined;
arr ; // arr = [];
注: unshift方法是会改变 数组的原始索引的
6、 js 变量提升、this、new
js超经典面试题Foo.getName()的故事
下面是一道超经典的JS面试题。 蕴含了静态属性与实例属性,变量提升,this指向,new一个函数的过程
function Foo() {
getName = function () {
console.log(1);
};
return this;
};
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
};
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
复制代码
输出一下结果
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
复制代码
一、解析: 1.Foo.getName()
我们先看此题的上半部分做了什么,首先定义了一个叫Foo的函数,之后为Foo创建了一个叫getName的静态属性存储了一个匿名函数,之后为Foo的原型对象新创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。
第一问的 Foo.getName 自然是访问Foo函数上存储的静态属性,自然是2
二、解析: 2.getName()
为何输出是4,这里考的是变量提升与函数声明提升。我们知道使用var声明变量会存在变量提升的情况,比如下面的例子中,即使在声明前使用变量a也不会报错,举例:
console.log(a)// undefined
var a = 1;
console.log(a)// 1
复制代码
因为声明提前会让声明提升到代码的最上层,而赋值操作停留在原地,所以上面代码等同于:
var a
console.log(a)// undefined
a = 1;
console.log(a)// 1
复制代码
而函数声明(注意是函数声明,不是函数表达式或者构造函数创建函数)也会存在声明提前的情况,即我们可以在函数声明前调用函数:
fn() // 1
function fn() {
console.log(1);
};
fn() // 1
//因为函数声明提前,导致函数声明也会被提到代码顶端,所以等同于
function fn() {
console.log(1);
};
fn() // 1
fn() // 1
复制代码
那这样就存在一个问题了,变量声明会提升,函数声明也会提升,谁提升的更高呢?在你不知道的JavaScript中明确指出,函数声明会被优先提升,也就是说都是提升,但是函数比变量提升更高,所以题目中的两个函数顺序可以改写成:
function getName() {
console.log(5);
};
var getName;
getName = function () {
console.log(4);
};
复制代码
这样就解释了为什么是输出4。
三、解析: 3.Foo().getName()
其实可以看出来,我们在执行Foo()函数的时候getName这个变量提升到外部的全局作用域中了,因为在js中,如果对于一个变量没用用var 或者 let等声明的话,他就默认是全局属性,就是window对象的一个属性。所以在这里我们的全局的getName又被改了 因为我们Foo()执行的时候返回了this而这里的this就是window对象 我们需要知道的是在浏览器中所有全局的声明都是window对象的属性和方法,所以这里我们调用this.getName()就会返回1了。
四、解析: 4.getName()
这里输出1已经毫无悬念,上一分析中,getName的值在Foo执行时被修改了,所以再调用getName一样等同于window.getName(),同样是输出1。
五、解析: 5.new Foo.getName()
首先还是先看运算符优先级吧,我自个看完的结果是【new Foo() > Foo() > new Foo】,先运算方式2的Foo.getName() 结果为“2”,再new一个Foo实例对象,因此这里new的过程就相当于单纯把Foo.getName执行了一遍输出2。
六、解析: 6.new Foo().getName()
这里考了new基本概念,首先这个调用分为两步,第一步new Foo()得到一个实例,第二步调用实例的getName方法。
我们知道new一个构造函数的过程大致为,以构造函数原型创建一个对象(继承原型链),调用构造函数并将this指向这个新建的对象,好让对象继承构造函数中的构造器属性,如果构造函数没有手动返回一个对象,则返回这个新建的对象。
所以在执行new Foo()时,先以Foo原型创建了一个对象,由于Foo.prototype上事先设置了一个getName方法(输出3的那个),所以这个对象可通过原型访问到这个方法,其次由于Foo内部也没提供什么构造器属性,最终返回了一个this(这个this指向实例),因此这里的this还是等同于我们前面概念提到的以Foo原型创建的对象,可以尝试输出这个实例,除了原型上有一个getName方法就没有其它任何属性,因此这里输出3。
七、解析: 7.new new Foo().getName()
相当于new(new Foo().getName()) 先执行new Foo().getName()由6部知道了输出3,再创建Foo.prototype.getName()的实例返回。结果为3
7、js 次方开方
Math.pow(3,2); 3的平方
Math.Pow(2,3); 2的立方
开方Math.sqrt(值)
如:
Math.sqrt(9); 9 开方,返回结果3
8、js对象的属性获取方式
1、不能是变量
var obj = {};
obj.AttrName = 'Tom'
注意:通过 对象.属性名 获取属性值的时候,属性名(AttrName )不能是一个变量。
2、可以是变量
原始数据数组:
var rawDataList =
[
{
"countDate": "2018-04-08",
"countNum": "2"
},
{
"countDate": "2018-04-18",
"countNum": "2"
},
{
"countDate": "2018-04-23",
"countNum": "7"
}
]
处理上述数据:
function Day2Mon2Year(dataList,prop){
for(var i = 0; i < dataList.length; i++){
var obj = rawDataList[i]
//报错:Uncaught TypeError: Cannot read property 'replace' of undefined
var dateAttr = obj.prop
//不报错:因为prop是变量,获取方式不能是通过.属性名称
var dateAttr = obj[prop]
}
}
//调用报错:Uncaught TypeError: Cannot read property 'replace' of undefined
Day2Mon2Year.(rawDataList,'countDate');
3、总结:
下面这个是重点,一般就直接用 第二种方式吧
一、如果属性名称是常量(固定值),获取属性值的方式有:
对象.属性名称对象[属性名称]
二、如果属性名称是一个变量(不固定值),获取属性值方式只能是:
对象[属性名称]
9、Promise中settimeout使用
Promise 中 setTimeout 的使用
function getData() {
return new Promise((resolve, reject) => {
setTimeout(resolve('hello'), 2000)
})
}
getData().then(res => {
console.log(res)
})
// 立马输出 hello
// code2
function getData() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'hello')
})
}
getData().then(res => {
console.log(res)
})
// 2s后输出hello
其实呢,这个差异就是 func() 和 func 的区别,setTimeout 的第一个参数是 func,如果用 func() 相当于其返回值为第一个参数。
这个地方应该是一个函数 func ,如果你传的是 func() ,代码解析器执行到此处的时候,就会立即执行这个函数,起不到延时的效果了。
常见的使用场景 实现一个 sleep 函数
// 1s 后执行的代码
const sleep = (time) => {
return new Promise(resolve => setTimeout(resolve, time))
}
sleep(1000).then(() => {
// 这里写你的操作
})
// 代码延时
const sleep = (time) => {
return new Promise(resolve => setTimeout(resolve, time))
}
async function sleepAsync() {
console.log('1')
let res = await sleep(1000)
console.log('2')
return res
}
sleepAsync()
10、前端框架路由实现的Hash和History两种模式的区别
- 之前面试的时候就有准备过前端框架中两种路由实现方式及区别,但是当时没专门下功夫去深入了解,就在网上搜了下别人总结的临时抱了下佛脚。但是事实证明,出来混,总是要还的,在后来的面试中又遇到了,而且这次问得更加深入,仅仅靠死记硬背来的知识总是遗忘得很快,网上别人总结的东西终归还是别人的,鉴于网上别人总结的内容有点杂乱无章,还是决定自己在此总结记录一番,以加深印象。
一、何为前端路由
- 路由的概念来自于服务器端,在SPA(单页应用)中,路由描述的是URL到函数的映射关系,即在浏览器中输入一个URL,相应的控制器会对提交的请求进行解析,然后进行路由匹配,找到对应的模块和函数进行执行。
二、如何实现
- 实现的两个核心问题是如何检测路由变化和如何改变URL而不刷新页面,通常有两种实现模式,一种是Hash模式,一种是History模式。
三、Hash模式
-
早期的前端路由的实现就是基于
location.hash来实现的,location.hash的值就是URL中#后面的内容 其实现原理就是监听#后面的内容来发起Ajax请求来进行局部更新,而不需要刷新整个页面。 -
使用
hashchange事件来监听 URL 的变化,以下这几种情况改变 URL 都会触发hashchange事件:浏览器前进后退改变 URL、<a>标签改变 URL、window.location改变URL。优缺点
-
兼容低版本浏览器,Angular1.x和Vue默认使用的就是hash路由
-
只有#符号之前的内容才会包含在请求中被发送到后端,也就是说就算后端没有对路由全覆盖,但是不会返回404错误
-
hash值的改变,都会在浏览器的访问历史中增加一个记录,所以可以通过浏览器的回退、前进按钮控制hash的切换
-
会覆盖锚点定位元素的功能
-
不太美观,#后面传输的数据复杂的话会出现问题
四、History模式
-
history 提供了
pushState和replaceState两个方法来记录路由状态,这两个方法改变 URL 不会引起页面刷新 -
history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或
<a>标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便。 -
pushState(state, title, url)和replaceState(state, title, url)都可以接受三个相同的参数: -
state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
-
title:标题,基本没用,一般传 null
-
url:设定新的历史记录的 url,新的 url 与当前 url 的 origin 必须是一样的,否则会抛错,url可以是绝对路径,也可以是相对路径。
优缺点
-
使用简单,比较美观
-
pushState()设置新的URL可以是任意与当前URL同源的URL,而hash只能改变#后面的内容,因此只能设置与当前URL同文档的URL -
pushState()设置的URL与当前URL一模一样时也会被添加到历史记录栈中,而hash#后面的内容必须被修改才会被添加到新的记录栈中 -
pushState()可以通过stateObject参数添加任意类型的数据到记录中,而hash只能添加短字符串 -
pushState()可额外设置title属性供后续使用 -
前端的URL必须和向发送请求后端URL保持一致,否则会报404错误
-
由于History API的缘故,低版本浏览器有兼容行问题
11、vue route的区别
一、
router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history对象。。。经常用的跳转链接就可以用this.$router.push,和router-link跳转一样。。。
this.$router.push会往history栈中添加一个新的记录。。详细见vue官方文档router.vuejs.org/zh/guide/es…
route相当于当前正在跳转的路由对象。。可以从里面获取name,path,params,query等。。
打印this.router。
路由传参的方式
1.可以手写完整的path:
this.router.push({path:`/user/{userId}`})
这样传递参数的话,配置路由的时候需要在path上加参数path:user/:userId。
这种接收参数的方式是this.$route.params.userId。
2.也可以用params传递:
3.也可以用query传递:
query传参是针对path的,params传参是针对name的。。接收参数的方式都差不多。。this.route.params.
注意这只是跳转url,跳转到这个url显示什么组件,得配置路由。router跳转和标签跳转,规则差不多。
展示上的话:
12、js中的三个点(...)
三个点(...)真名叫扩展运算符,是在ES6中新增加的内容,它可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开;还可以在构造字面量对象时将对象表达式按照key-value的方式展开
字面量一般指[1,2,3]或者{name:'chuichui'}这种简洁的构造方式,多层嵌套的数组和对象三个点就无能为力了
说白了就是把衣服脱了,不管是大括号([])、花括号({}),统统不在话下,全部脱掉脱掉!
// 数组
var number = [1,2,3,4,5,6]
console.log(...number) //1 2 3 4 5 6
//对象
var man = {name:'chuichui',height:176}
console.log({...man}) / {name:'chuichui',height:176}
有什么用?
它的用处很广泛,我们随处都可以看到,下面是几个常见的例子
复制用它
//数组的复制
var arr1 = ['hello']
var arr2 =[...arr1]
arr2 // ['hello']
//对象的复制
var obj1 = {name:'chuichui'}
var obj2 ={...arr}
ob12 // {name:'chuichui'}
合并用它
//数组的合并
var arr1 = ['hello']
var arr2 =['chuichui']
var mergeArr = [...arr1,...arr2]
mergeArr // ['hello','chuichui']
// 对象分合并
var obj1 = {name:'chuichui'}
var obj2 = {height:176}
var mergeObj = {...obj1,...obj2}
mergeObj // {name: "chuichui", height: 176}
字符转数组用它
var arr1 = [...'hello']
arr1 // ["h", "e", "l", "l", "o"]
函数传参用它
可以和正常的函数相结合,灵活使用
function f(v,w,x,y,z){ }
var args = [2,3]
f(1,...args,4,...[5])
当我们想把数组中的元素迭代为函数参数时,用它!
function f(x,y,z){}
var args = [1,2,3]
f(...args)
// 以前的方法
f.apply(null,args);
13、js 的数据类型(typeof、instanceof)
最新的 ECMAScript 标准定义了 9 种数据类型:
- 6 种原始类型,使用typeof运算符检查:
- null:
typeof instance === "object"。 - Object:
typeof instance === "object"。任何 constructed 对象实例的特殊非数据结构类型,也用做数据结构:new Object,new Array,new Map,new Set,new WeakMap,new WeakSet,new Date,和几乎所有通过 new keyword 创建的东西。 - Function:非数据结构,尽管 typeof 操作的结果是:
typeof instance === "function"。这个结果是为 Function 的一个特殊缩写,尽管每个 Function 构造器都由 Object 构造器派生。
记住 typeof 操作符的唯一目的就是检查数据类型,如果我们希望检查任何从 Object 派生出来的结构类型,使用 typeof 是不起作用的,因为总是会得到 "object"。检查 Object 种类的合适方式是使用 instanceof 关键字。但即使这样也存在误差。
基本类型(单类型):除Object。 String、Number、boolean、null、undefined。
引用类型:object。里面包含的 function、Array、Date。
JS数据类型:JS 中 typeof 输出分别是什么?
{ } 、[ ] 输出 object。也就是数组 对象都判断为 object
console.log( ) 输出 function。
检测数组类型的方法
① instanceof 操作符
③ Array.isArray( ) 检验值是否为数组
14、typeof 已经一些格式
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
下表总结了 typeof 可能的返回值。有关类型和原始值的更多信息,可查看 JavaScript 数据结构 页面。
| 类型 | 结果 |
|---|---|
| Undefined | "undefined" |
| Null | "object" (见下文) |
| Boolean | "boolean" |
| Number | "number" |
| BigInt(ECMAScript 2020 新增) | "bigint" |
| String | "string" |
| Symbol (ECMAScript 2015 新增) | "symbol" |
| 宿主对象(由 JS 环境提供) | 取决于具体实现 |
| Function 对象 (按照 ECMA-262 规范实现 [[Call]]) | "function" |
| 其他任何对象 | "object" |
一定需要注意的是 typeof 的返回值以及 判断值 都是小写的 ,而且要带双引号
实例
// 数值
typeof 37 === 'number';
// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
// 布尔值
typeof true === 'boolean';
// Symbols
typeof Symbol() === 'symbol';
// Undefined
typeof undefined === 'undefined';
// 对象
typeof {a: 1} === 'object';
// 函数
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';