最后
中年危机是真实存在的,即便有技术傍身,还是难免对自己的生存能力产生质疑和焦虑,这些年职业发展,一直在寻求消除焦虑的依靠。
-
技术要深入到什么程度?
-
做久了技术总要转型管理?
-
我能做什么,我想做什么?
-
一技之长,就是深耕你的专业技能,你的专业技术。(重点)
-
独立做事,当你的一技之长达到一定深度的时候,需要开始思考如何独立做事。(创业)
-
拥有事业,选择一份使命,带领团队实现它。(创业)
一技之长分五个层次
-
栈内技术 - 是指你的前端专业领域技术
-
栈外技术 - 是指栈内技术的上下游,领域外的相关专业知识
-
工程经验 - 是建设专业技术体系的“解决方案”
-
带人做事 - 是对团队协作能力的要求
-
业界发声 - 工作经验总结对外分享,与他人交流
永远不要放弃一技之长,它值得你长期
信仰持有
。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
主要内容包括html,css,html5,css3,JavaScript,正则表达式,函数,BOM,DOM,jQuery,AJAX,vue 等等。
newelementX 可选。可添加多个元素。
push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObject,而不是创建一个新的数组。push() 方法和 pop() 方法使用数组提供的先进后出栈的功能。
Undefined、Null、Bollean、Number、String
前者会自动转换类型
后者不会
语法结构上不同,
for 一般用来遍历数组的,是比较简单的操作
for in 一般用来遍历对象,虽然for in 也能遍历数组,但是会存在
以下几个问题:
1、index索引为字符串型数字,不能直接进行几何运算
2、遍历顺序有可能不是按照实际数组的内部顺序
3、使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗
的原型方法method和name属性
这也是为什么用for不用for in的区别,如果是遍历普通数组的话,
用for是最好的选择,但是如果是对象,用for in就好了。
操作数1 == 操作数2, 操作数1 === 操作数2
双等号==:
(1)如果两个值类型相同,再进行三个等号(===)的比较
(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
1)如果一个是null,一个是undefined,那么相等
2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较
三等号===:
(1)如果类型不同,就一定不相等
(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相 等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
(4)如果两个值都是true,或是false,那么相等
(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等
(6)如果两个值都是null,或是undefined,那么相等
for in:
1.for…in 语句用于对数组或者对象的属性进行循环操作。
2.for … in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。
3.for…in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
for :
1.for循环是对数组的元素进行循环,而不能引用于非数组对象。
第一种:
function uniq(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));
第二种:对象键值法去重
function uniq(array){
var temp = {}, r = [], len = array.length, val, type;
for (var i = 0; i < len; i++) {
val = array[i];
type = typeof val;
if (!temp[val]) {
temp[val] = [type];
r.push(val);
} else if (temp[val].indexOf(type) < 0) {
temp[val].push(type);
r.push(val);
}
}
return r;
}
var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];
console.log(uniq(aa));
第三种:排序后相邻去除法
function uniq(array){
array.sort();
var temp=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== temp[temp.length-1]){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];
console.log(uniq(aa));
第四种:数组下标法
function uniq(array){
var temp = [];
for(var i = 0; i < array.length; i++) {
//如果当前数组的第i项在当前数组中第一次出现的位置是i,才存入数组;否则代表是重复的
if(array.indexOf(array[i]) == i){
temp.push(array[i])
}
}
return temp;
}
var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];
console.log(uniq(aa));
第五种:优化遍历数组法
function uniq(array){
var temp = [];
var index = [];
var l = array.length;
for(var i = 0; i < l; i++) {
for(var j = i + 1; j < l; j++){
if (array[i] === array[j]){
i++;
j = i;
}
}
temp.push(array[i]);
index.push(i);
}
console.log(index);
return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));
第一种:冒泡排序:
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
快速排序:
var arr = [1,4,-8,-3,6,12,9,8];
function quicksort(arr){
if(arr.length <= 1){
return arr;
}
var middleIndex = Math.floor(arr.length/2);
var middleNum = arr.splice(middleIndex,1);
var left = [], right = [];
for (var i = 0; i < arr.length; i++) {
if(arr[i] < middleNum){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quicksort(left).concat(middleNum, quicksort(right));
}
console.log(quicksort(arr));
选择排序:
var arr = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){
for(var i=0;i<arr.length;i++){
//设置当前范围最小值和索引
var min = arr[i];
var minIndex = i;
//在该范围选出最小值
for(var j=i+1;j<arr.length;j++){
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//将最小值插入,并将原来位置的最小值删除
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
return arr;
}
console.log(selectSort(arr));
插入排序:
var array = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){
for(var i=0;i<arr.length;i++){
//设置当前范围最小值和索引
var min = arr[i];
var minIndex = i;
//在该范围选出最小值
for(var j=i+1;j<arr.length;j++){
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//将最小值插入,并将原来位置的最小值删除
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
}
selectSort(array);
document.write(array);
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
在谈原型链之前,我们首先要了解自定义函数与 Function 之间是什么关系,而构造函数、原型和实例之间又存在什么千丝万缕的关系呢?其实,所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,该属性也是一个对象;那么在原型对象上有一个 constructor 属性,该属性指向的就是构造函数;而实例对象上有一个 proto 属性,该属性也指向原型对象,并且该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QqhVpWo7-1630416218424)(en-resource://database/450:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8tp8hnPS-1630416218430)(en-resource://database/452:1)]
1、原型链
1)构造函数、原型和实例的关系
①构造函数都有一个属性prototype,这个属性是一个对象(Object的实例)
②原型对象prototype里面有一个constructor属性,该属性指向原型对象所属的构造函数
③实例对象都有一个_proto_属性,该属性也指向构造函数的原型对象,它是一个非标准属性,
不可以用于编程,它是用于浏览器自己使用的
2)prototype与_proto_的关系
①prototype是构造函数的属性
②_proto_是实例对象的属性 ——这两者都指向同一个对象
【总结】
i)函数也是对象,对象不一定是函数;
ii)对象的本质:无序的键值对集合;键值对当中的值可以是任意数据类型的值
iii)对象就是一个容器,这个容器当中放的是(属性和方法)
3)属性搜索
①在访问对象的某个成员的时候会先在对象中找是否存在
②如果当前对象中没有就在构造函数的原型对象中找
③如果原型对象中没有找到就到原型对象的原型上找
④知道Object的原型对象的原型是null为止
2、Function——
所有函数都是Function的实例
`①本地对象:独立于宿主环境(浏览器)的对象——包括Object、Array、Date、RegExp、 Function、Error、Number、String、Boolean
②内置对象——包括Math、Global(window,在js中就是全局变量),使用的时候不需要 new
③宿主对象——包括自定义对象、DOM、BOM
第一种: new关键字改变this指向
function Fn(){
this.user = “追梦子”;
}
var a = new Fn();
console.log(a.user); //追梦子
用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份第二种: call()
第二种: call()
var a = {
user:“追梦子”,
fn:function(){
console.log(this.user); //追梦子
}
}
var b = a.fn;
b.call(a); //若不用call,则b()执行后this指的是Window对象
把b添加到第一个参数的环境中,简单来说,this就会指向那个对象。
第三种:apply()
var a = {
user:“追梦子”,
fn:function(){
console.log(this.user); //追梦子
}
}
var b = a.fn;
b.apply(a);
第四种:bind()
var a = {
user:“追梦子”,
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代码没有被打印
我们发现代码没有被打印,对,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。
- 变量声明
let 与 const:
可以把let看成var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即无法被更改值的变量。
for (let i=0;i<2;i++)console.log(i);//输出: 0,1
console.log(i);//输出:undefined,严格模式下会报错
2.类的支持
ES6中添加了对类的支持,引入了class关键字(其实class在JavaScript中一直是保留字,目的就是考虑到可能在以后的新版本中会用到,现在终于派上用场了)。JS本身就是面向对象的,ES6中提供的类实际上只是JS原型模式的包装。现在提供原生的class支持后,对象的创建,继承更加直观了,并且父类方法的调用,实例化,静态方法和构造函数等概念都更加形象化。
//类的定义
class Animal {
//ES6中新型构造器
constructor(name) {
this.name = name;
}
//实例方法
sayName() {
console.log('My name is '+this.name);
}
}
//类的继承
class Programmer extends Animal {
constructor(name) {
//直接调用父类构造器进行初始化
super(name);
}
program() {
console.log(“I’m coding…”);
}
}
//测试我们的类
var animal=new Animal(‘dummy’),
wayou=new Programmer(‘wayou’);
animal.sayName();//输出 ‘My name is dummy’
wayou.sayName();//输出 ‘My name is wayou’
wayou.program();//输出 ‘I’m coding…’
3.字符串模板
字符串模板相对简单易懂些。ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。如果你使用过像C#等后端强类型语言的话,对此功能应该不会陌生。
//产生一个随机数
var num=Math.random();
//将这个数字输出到console
console.log(your num is ${num}
);
4.解构:
自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。
var [x,y]=getVal(),//函数返回值的解构
[name,age]=[‘wayou’,‘male’,‘secrect’];//数组解构
function getVal() {
return [ 1, 2 ];
}
console.log(‘x:’+x+‘, y:’+y);//输出:x:1, y:2
console.log(‘name:’+name+‘, age:’+age);//输出: name:wayou, age:secrect
5.Promise:
Promises是处理异步操作的一种模式,之前在很多三方库中有实现,比如jQuery的deferred 对象。当你发起一个异步请求,并绑定了.when(), .done()等事件处理程序时,其实就是在应用promise模式。
//创建promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if ( /*如果成功 */ ) {
resolve(“Stuff worked!”);
} else {
reject(Error(“It broke”));
}
});
//绑定处理程序
promise.then(function(result) {
//promise成功的话会执行这里
console.log(result); // “Stuff worked!”
}, function(err) {
//promise失败会执行这里
console.log(err); // Error: “It broke”
});
ES6提供的解决异步处理方法
有两个优点
1.promise对象的状态不受外界影响
-pending 初始状态
-fulfilled 成功状态
-rejected 失败状态
2.promise的状态一旦改变,就不会再变,状态不可逆,只能由pending变成pending变成fulfilled或者由pending变成rejected
三个缺点
1.无法取消promise,一旦新建它就会立即执行,无法中途取消
2.如果不设置回调函数,promise内部抛出的错误,不会反映到外部
3.当处于pending状态时,无法得知目前进展到哪一个阶段
用法
const promise = new Promise(function(resolve, reject) {
// … some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。同源指的是协议,域名和端口号均相同则属于同源
使用jsonp跨域,因为script标签引入的js是不受同源策略的限制,通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件,此时文件返回一个JS函数的调用
通过cors跨域,实现cors通信的关键是服务器,只要服务器实现cors接口,就可以跨域
反向代理跨域,反向代理指的是在前端的服务器环境中, 短暂的开启一个后端服务器, 由后端服务器进行数据请求, 然后在将结果返回给前端
模块化的开发方式可以提高代码复用率,方便进行代码的管理。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
这种区别各有优劣,只是语法上的差距,而且requireJS和SeaJS都支持对方的写法
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同
highcharts http://www.highcharts.com/
jscharts http://www.jscharts.com/
AdminLTE http://adminlte.la998.com/
库和框架都是一种有别于软件、面向程序开发者的产品形式。
库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫
类库。
框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类
或函数,即可实现全部功能。
柯里化(英语:Currying),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。
具体内容见 https://juejin.im/entry/58b316d78d6d810058678579
1)意外的全局变量引起的内存泄露
function leak () {
leak=“xxx”; //leak成为一个全局变量,不会被回收 相当于 window.leak = ‘XXX’
}
-
闭包可以维持函数内局部变量,使其得不到释放。
-
没有清理的DOM元素引用
-
被遗忘的定时器或者回调
-
定义局部变量.查找局部变量比全局变量要快。
-
不滥用闭包。
-
合并js文件,减少http请求
-
避免使用for-in循环
-
尽量不用with,eval语句,try-catch的catch子句要谨慎使用
- 闭包经典使用场景一:通过循环给页面上多个dom节点绑定事件
Button0
Button1
Button2
Button3
Button4
for(var i = 0, len = btns.length; i < len; i++) {
(function(i) {
btns[i].onclick = function() {
alert(i);
}
}(i))
}
-
封装变量 闭包可以将一些不希望暴露在全局的变量封装成“私有变量”。
-
闭包使用场景三:延续局部变量的寿命
闭包工具库:???
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
举例:
console.log (a) //由于变量提升,输出undefined
var a
console.log(a) //报错 ReferenceError: a is not defined
let a
ES6规定,let/const 命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。
总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。
这在语法上,称为 “暂时性死区”( temporal dead zone,简称 TDZ)。
-
栈(stack):为自动分配的内存空间,他由系统自动释放。存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。基本的数据类型放在栈中
-
堆(heap):则是动态分配的内存,大小不定也不会自动释放。引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况进行特定的分配。
- 箭头函数默认不会使用自己的this,而是会和外层的this保持一致,最外层的this就是window对象。在多层对像嵌套里箭头函数里this是和最最外层保持一致的
-
浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存
-
深拷贝:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。
41.手写一个promise,采用es5的方面。promise的源码理解
var fn=function(resolve, reject){
console.log(‘begin to execute!’);
var number=Math.random();
if(number<=0.5){
resolve(‘less than 0.5’);
}else{
reject(‘greater than 0.5’);
}
}
var p=new Promise(fn);
p.then(function(data){
console.log('resolve: ', data);
}, function(data){
console.log('reject: ', data);
})
对promise源码的理解:
当我们运行 var p=new Promise(fn) 这条语句的时候,fn函数就已经执行了,然而,p.then这个方法是在后面才定义了resolve和reject,那么为何fn函数能够知道resolve和reject函数是什么呢?换句话说,resolve和reject函数是如何回到过去,出现在先执行的fn函数当中的呢?要解决这个问题,主要运用的就是setTimeout这个方法,来延迟fn当中resolve和reject的执行。我们知道js是单线程+消息队列,必须等主线程代码执行完毕才能开始执行消息队列当中的代码。因此,会首先执行then这个方法,给里面两个参数赋值。
加入状态:pending, resolved, rejected
在Promise规范当中,规定Promise只能从初始pending状态变到resolved或者rejected状态,是单向变化的,也就是说执行了resolve就不会再执行reject,反之亦然。并在必要的地方进行判断,防止重复执行。
function MyPromise(fn) {
this.value;
this.status = ‘pending’;
this.resolveFunc = function() {};
this.rejectFunc = function() {};
fn(this.resolve.bind(this), this.reject.bind(this));
}
MyPromise.prototype.resolve = function(val) {
var self = this;
if (this.status == ‘pending’) { //判断状态
this.status = ‘resolved’;
this.value=val;
setTimeout(function() {
self.resolveFunc(self.value);
}, 0);
}
}
MyPromise.prototype.reject = function(val) { //判断状态
var self = this;
if (this.status == ‘pending’) {
this.status = ‘rejected’;
this.value=val;
setTimeout(function() {
self.rejectFunc(self.value);
}, 0);
}
}
MyPromise.prototype.then = function(resolveFunc, rejectFunc) {
this.resolveFunc = resolveFunc;
this.rejectFunc = rejectFunc;
}
链式调用:
要实现链式调用,then方法的返回值也必须是一个Promise对象,这样才能再次在后面调用then。
-
在函数前有一个关键字
async
,await
关键字只能在使用async
定义的函数中使用。任何一个async
函数都会隐式返回一个promise
,并且promise resolve 的值就是 return 返回的值 -
不能在函数开头使用
await
-
Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。
-
Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(
then
、catch
等等),操作本身的语义反而不容易看出来。
- async函数就是generator函数的语法糖
(1) 箭头函数与function定义函数的写法:
//function
function fn(a, b){
return a + b;
}
//arrow function
var foo = (a, b)=>{ return a + b };
(2) this的指向:
使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。
(3) 构造函数
function是可以定义构造函数的,而箭头函数是不行的。
(4) 变量提升
由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let const定义的时候更不必说)关键词,而var所定义的变量不能得到变量提升,故箭头函数一定要定义于调用之前!
(1) 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
(2)方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象,箭头函数没有this
因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表
- 解构赋值,即对某种结构进行解析,然后将解析出来的值赋值给相关的变量,常见的有数组、对象、字符串的解构赋值等
- 数组解构
只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
- 对象结构
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: ‘aaa’, bar: ‘bbb’ };
foo // “aaa”
bar // “bbb”
- 字符串结构
字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = ‘hello’;
a // “h”
b // “e”
c // “l”
d // “l”
e // “o”
- 负载
- 概念
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。扩展运算符与正常的函数参数可以结合使用,后面也可以放置表达式,但如果后面是一个空数组,则不产生任何效果。
let arr = [];
arr.push(…[1,2,3,4,5]);
console.log(arr); //[1,2,3,4,5]
console.log(1, …[2, 3, 4], 5) //1 2 3 4 5
console.log(…(1 > 0 ? [‘a’] : [])); //a
console.log([…[], 1]); //[1]
- 应用
1 替代函数的apply方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(…[14, 3, 77])
2 复制数组
// ES5 的写法
const a1 = [1, 2];
const a2 = a1.concat();
// ES6 的写法
const a1 = [1, 2];
onst a2 = […a1]; //或 const […a2] = a1;
3 合并数组
// ES5 的写法
[1, 2].concat(more);
arr1.concat(arr2, arr3);
// ES6 的写法
[1, 2, …more];
[…arr1, …arr2, …arr3]
4 与结构赋值结合
// ES5 的写法
a = list[0], rest = list.slice(1)
// ES6 的写法
[a, …rest] = list
答:对数组进行深拷贝:
-
for循环
-
slice方法
-
concat方法
-
ES6扩展运算符
对对象进行深拷贝:
-
for循环
-
先转换成json在转换成对象
-
ES6扩展运算符
答:深拷贝
最后
由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
b’ };
foo // “aaa”
bar // “bbb”
- 字符串结构
字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = ‘hello’;
a // “h”
b // “e”
c // “l”
d // “l”
e // “o”
- 负载
- 概念
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。扩展运算符与正常的函数参数可以结合使用,后面也可以放置表达式,但如果后面是一个空数组,则不产生任何效果。
let arr = [];
arr.push(…[1,2,3,4,5]);
console.log(arr); //[1,2,3,4,5]
console.log(1, …[2, 3, 4], 5) //1 2 3 4 5
console.log(…(1 > 0 ? [‘a’] : [])); //a
console.log([…[], 1]); //[1]
- 应用
1 替代函数的apply方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(…[14, 3, 77])
2 复制数组
// ES5 的写法
const a1 = [1, 2];
const a2 = a1.concat();
// ES6 的写法
const a1 = [1, 2];
onst a2 = […a1]; //或 const […a2] = a1;
3 合并数组
// ES5 的写法
[1, 2].concat(more);
arr1.concat(arr2, arr3);
// ES6 的写法
[1, 2, …more];
[…arr1, …arr2, …arr3]
4 与结构赋值结合
// ES5 的写法
a = list[0], rest = list.slice(1)
// ES6 的写法
[a, …rest] = list
答:对数组进行深拷贝:
-
for循环
-
slice方法
-
concat方法
-
ES6扩展运算符
对对象进行深拷贝:
-
for循环
-
先转换成json在转换成对象
-
ES6扩展运算符
答:深拷贝
最后
由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-MqT0RVJf-1715416766106)]
[外链图片转存中…(img-MQiIcpGi-1715416766107)]