php 对象 字面量,JS对象、模板字面量与流程控制

JS对象、模板字面量与流程控制

一、JavaScript值传递与引用传递的区别

值传递与引用传递的两种应用场景

1、赋值

1.1 值传递:应用于原始类型:包括字符串、数值、布尔。

let a=1;

let b=a;

console.log("a = %d, b = %d",a,b);// a = 1, b = 1

现在我们对a变量进行一个更新:

a=2;

console.log("a = %d, b = %d",a,b);// a = 2, b = 1

从上述案例结果可以看出,把变量a的值更新为2,b还是1,说明更新a的值是不影响b的,因此,原始类型是值传递,仅仅是传递了一个值,并没有把地址传过去。

1.2 引用传递:应用于引用类型,包括对象(object)、数组(array)。

let obj1={a:1,b:2}

console.log(obj1);// a = 1, b = 2

let obj2=obj1;

console.log(obj2);// a = 1, b = 2

现在更新obj1的值:obj1.a = 10,那么obj2也会进行同步更新,因为对象时引用传递的。

obj1.a=10;

console.log(obj1);// a = 10, b = 2

console.log(obj2);// a = 10, b = 2

有这么一种情况,

constf2=x=(x.a=10);

let o={a:1,b:2};

console.log(o);// a: 1, b: 2

// 调用f2函数

f2(o);

// 打印对象o的值

console.log(o);// a: 10, b: 2

此时,对象o看起来是函数中对于o.a的更新生效了,实际上仍是值传递。

对于引用类型,只有全新复制才算是更新,修改属性不算。例如以下案例就不是更新变量:

constobj={x:1,y:2};

obj.x=20;

以下操作才是更新变量:

obj={};

constf3=x=>(x={});

f(o);

console.log(o);// a: 10, b: 2

- 函数中对于对象参数/引用参数的更新并没有影响到入参。

2、传参:传参时,不论是什么类型,都是“值传递”constf1=x=>(x=10);

let m=5;

console.log("m = %d",m);

// 调用函数f1

f1(m);// m = 5

入参:调用函数时传入的参数,简称“入参”。

函数中对参数的更新,并不会影响到入参。

深拷贝:值传递

浅拷贝:引用传递

二、解构赋值ES6中允许按照一定模式从数组和对象中提取值,然后再对变量进行赋值,这样的操作称为解构赋值。即:快速从集合数据(数组/对象)解构独立变量。

在ES6以前,在变量赋值的时候只能为变量指定对应的值:

let a=1;

let b=2;

let c=3;

从ES6以后,允许用以下方式写:

let[a,b,c]=[1,2,3];

以上代码的意思是从数组中取值,按照对应的位置给变量赋值,此时:

a = 1,b = 2,c = 3

从本质上来说,ES6的这种写法类似于“模式匹配”,所以,解构需要等号两边类型一致,左边的变量就会被赋予相对应的值。

2.1 数组的解构赋值

使用嵌套数组进行解构

let[a,[[b],c]]=[1,[[2],3]];

结果:

a = 1,b = 2,c = 3

let[,,third]=["one","two","three"];

结果:

third的值为:”three”

let[first,,third]=["one","two","three"];

结果:

first = “one”, thrid = “three”

解构赋值允许指定默认值:

let[x,y='100']=[10];

结果:

x = 10,y = 100

关于在指定默认值的时候有一个坑,具体请看下面代码:

let[x=1]=[undefined];

console.log(x);

let[x=1]=[null];

console.log(x);

上述两块代码x的值分别是多少呢?我当时没有在代码中测试,猜的结果都是1,但是在控制台试了一下,发现并不都是1,而是1和null,结果如图:

6b045279557aec0cd4b3d254bdc5e60b.png

2d1cabdeb09ac1762af01f32632ba31b.png

这是为什么呢?通过查阅相关资料发现:

ES6内部使用的是严格相等运算符(===)来判断某个位置是否有值,所以,当一个数组变量中存在不严格等于undefined的值时,默认值是不生效的,怎么理解这句话呢?其实很好理解,我们在控制台测试一下null和undefined是否严格相等即可,例如:

undefined == null // true

undefined === null // false

测试效果:

3c721b907a98f156be8971f0f5f060e7.png

2.2 对象的解构赋值

解构不仅可以用于数组,还可以用于对象。

现有如下对象:

constuser={

name:'残破的蛋蛋',

age:18,

sex:'男',

weight:500

};

现在我想单独把姓名和年龄两个对象属性拿出来并赋值,可以这样写:

({age,name}={name:'残破的蛋蛋',age:18});

console.log(name,age);// 残破的蛋蛋 18

对象的解构与数组不同的是:数组的元素是按次序排列的,变量的值是由它的位置决定的,但是对象的属性没有固定的次序,但是变量名称必须与属性名称相同才能取到值。

let{gender}={name:'残破的蛋蛋',age:18});

console.log(gender);// undefined

上述代码中,由于左边的变量在右边没有对应的同名属性,导致无法正确的取到值,所以结果为undefined。

如果变量名与属性名不一致,必须按照如下格式写:

({sex:gender}={name:'残破的蛋蛋',age:18,sex:'男'});

console.log(gender)

相当于是给sex起了一个别名gender。

对象字面量的简化let userInfo={

username:'残破的蛋蛋',

email:'admin@php.cn',

getUserInfo:()=>{

return`${this.username}${this.email}`

}

};

console.log(user.getUserInfo());// 残破的蛋蛋 admin@php.cn

let{username,email}=userInfo;

console.log(username,email);// 残破的蛋蛋 admin@php.cn

当属性与同一个作用域的变量名相同时,可以省略变量名称,使用属性来引用变量值。同时,方法也能简化,省略冒号和function 关键字

总结:箭头函数中的this总是指向定义它时的作用域(静态作用域/词法作用域),而并非调用时的作用域。

由于userInfo对象不能创建作用域,因此this指向了userInfo的作用域/作用域链: window,全局中没有username,email这两个变量,所以输出undefined。

userInfo={

// username: username,

username,

// email: email,

email,

// 方法也能简化:

getInfo(){

return`${this.userName}(${this.userEmail})`;

},

};

2.3 字符串的解构赋值

字符串也是可以解构赋值的,因为在解构赋值种,字符串被转换成了一个类数组的对象。

const[a,b,c,d,e]='Hello';

console.log(a,b,c,d,e);// H e l l o

三、call,apply,bind的区别与联系call,apply,bind的作用是改变函数运行时的this指向问题

现有如下函数和对象:

functionhello(name){

console.log(this);

this.name=name;

console.log(this.name);

}

let obj={

name:'admin'

};

现在,如果我想在对象obj里添加一个hello方法,我需要再写一遍hello吗?这样的话不就太繁琐了吗?但是使用bind,call,apply就可以很好的解决这个问题。

3.1 使用bind把函数hello绑定到对象中语法:第一个参数是this的指向,从第二个参数开始是接收的参数列表。

let f=hello.bind(obj,'残破的蛋蛋');

console.log(f());

需要注意的是,bind返回的是一个函数的源代码,所以console.log(f)返回的就是hello函数的源代码,故需要加一个圆括号表示调用。

3.2 使用call把函数hello绑定到对象中第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。

f=hello.call(obj,'残破的蛋蛋2');

3.3 使用apply把函数hello绑定到对象中

apply()函数接收两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。

f=hello.apply(obj,['残破的蛋蛋3']);

总结

call、apply、bind函数的区别:bind返回的是函数的源代码;apply和call是立即调用。

apply和call用法基本相同,唯一的差别在于:当需要传多个变量时,call接受的是多个单一变量,而apply接受的是一个数组。

不能在箭头函数里使用call和apply,因为对于箭头函数来说,其内部的this永远指向定义它时所在的对象,而不是调用它的对象。

四、访问器属性的原理与实现过程constproduct={

data:[

{name:"iPhone12",price:6499,num:10},

{name:"MacBook Pro",price:16499,num:2},

{name:"iPad mini",price:5499,num:5},

],

getAmounts(){

returnthis.data.reduce((t,c)=>(t+=c.price*c.num),0);

},

test(){

console.log(this);

},

test1:()=>{

console.log(this);

}

};

console.log(product.test());// product

console.log(product.test1());// window

可以使用访问器属性,将方法伪造成一个属性去访问:

gettotal(){

returnthis.data.reduce((t,c)=>(t+=c.price*c.num),0);

}

colsole.log(product.total);

五、JavaScript流程控制方法JavaScript流程控制可以简单的理解为:控制代码按照一定的结构顺序来执行,流程控制主要分为三种,分别是:顺序结构、分支结构、循环结构

5.1 顺序流程控制

顺序结构是程序中最简单、最基本的流程控制,程序会按照代码的先后顺序进行执行,程序中大部分的代码都是这样执行的。

5.2 分支结构JavaScript提供了两种最常用的分支结构语句:if和switch语句。

5.2.1 if语句单分支语句(if)

if(条件表达式){

// 条件成立需要执行的代码块

}

案例:根据学生的成绩判断是及格、优秀、还是不及格需要补考:

学生成绩在60分以下需要补考,60-80之间为及格,80-100之间为优秀,成绩小于0或者大于100提示成绩分数非法!

let score=80;

if(score>=60){

console.log('及格了');// 及格了

}

双分支语句 (if…else)

if(条件表达式){

// 如果条件成立需要执行的代码块

}else{

// 否则执行该代码块

}

let score=59;

if(score>=60){

console.log('及格了');

}else{

console.log('补考吧,兄弟');

}

// 补考吧,兄弟

多分支语句(if…else if)

if(条件表达式1){

// 代码块1

}elseif((条件表达式2)){

// 代码块2

}elseif((条件表达式3)){

// 代码块3

}else{

// 上述条件都不成立时,执行该代码块

}

案例:

let score=90;

if(score>=60&&score<80){

console.log('合格');

}elseif(score>=81&&score<=100){

console.log('学霸');

}elseif(score>100||score<0){

console.log('非法分数');

}else{

console.log('补考吧,兄弟');

}

// 学霸

5.2.2 switch语句switch根据不同的条件来执行不同的代码

语法:

switch(条件表达式){

case条件1:

// 条件表达式 === 条件1时执行的代码

break;

case条件2:

// 条件表达式 === 条件2时执行的代码

break;

case条件3:

// 条件表达式 === 条件3时执行的代码

break;

default:

// 条件表达式 !== 以上任一条件时执行

}

上述学生成绩的案例也可以使用switch语句来进行改写:

let score=90

switch(true){

casescore>=60&&score<80:

console.log('合格');break;

casescore>=81&&score<=100:

console.log('学霸');break

// 判断成绩是否是一个有效的分数?

casescore>100||score<0:

console.log('非法分数');break

// 默认分支

default:console.log('补考吧,兄弟');

}

结果:学霸

break 关键字

在JS代码中,break关键字是用来中断代码执行的,在switch…case语句中,如果找到了符合条件的语句,就会中断执行。因此,在使用switch语句时,千万不要忘记写break了。

default 关键字

如果给出的条件在case中都不满足,就运行default下的代码。

switch…case语句使用的是严格比较(===),意思是值必须与要匹配的类型相同。只有操作数属于同一类型时,严格比较才能为 true。

5.3 循环结构

5.3.1 for循环语法:

for(语句1;语句2;语句3){

// 执行的代码块

}

其中,这三条语句的执行顺序分别为:

语句1:在循环开始之前执行,相当于初始化;

语句2:循环语句的条件;

语句3:在循环执行之后再执行。

实例:

// 定义一个数组

constarr=[1,2,3,4,5];

for(let i=0;i

document.write(arr[i]);

}

结果:1 2 3 4 5

- 关于这三条语句的个人理解:

一般情况下,语句1是用来初始化循环中所用的变量的,比如上述实例中初始化了i = 0。但是语句1,并不是只能初始化一个值,也可以初始化多个值,用逗号分隔,比如:

for(i=0,len=arr.length;i

text+=arr[i]+"
";

}

而且,当在循环开始前,已经初始化过变量,那么语句1还能省略:

let i=0,len=arr.length;

for(;i

document.write(arr[i]);

}

语句2用于计算初始化中的条件,如果语句2返回的是true,那么循环继续,如果返回的是false,那么就结束循环。

语句3是将初始值递增/递减的,同时,语句3也可以省略:

let i=0,len=arr.length;

for(;i

document.write(arr[i]);

i++;

}

5.3.2 for…in循环

在JavaScript中,for…in语句遍历对象属性:

语法:

for(变量in对象){

在此执行代码

}

解释:

变量可以使数组元素,也可以是对象的属性

实例:声明一个用户的信息对象:

constuser={

name:'残破的蛋蛋',

age:18,

sex:'男',

weight:500

};

现在要求我们把用户的信息打印出来,使用for..in可以这样操作:

for(let key in user){

console.log(`${key}=>${user[key]}`);

}

控制台打印的效果:

fc11a70f5cd936337267fe1f78fe68f6.png

5.3.3 三元运算符语法:

条件 ?表达式1:表达式2

意义:

如果条件为真,则执行表达式1,否则执行表达式2

案例:

let score=80;

score>=60?'及格':'不及格';

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值