js 急速入门之二
值传递与引用传递
// 1. 赋值
// 1.1 值传递:原始类型(string,number,bool)
let a = 1;
let b = a;
console.log("a=%d,b=%d", a, b);
a = 2;
console.log("a=%d,b=%d", a, b);
// 1.2 引用传递:引用类型(object,array)
let obj1 = { a: 1, b: 2 };
console.log(obj1);
let obj2 = obj1;
obj1.a = 2;
console.log(obj2);
// 引用类型指向的是相同内存地址
// 2. 传参
// 传参时,不论什么类型,都是“值传递”
const f1 = (x) => (x = 10);
let m = 5;
f1(m);
// 入参:调用函数时传入的参数,简称:入参
// 函数中对参数的更新,并不会影响到入参
console.log("m = %d", m);
const f2 = (x) => (x.a = 10);
let o = { a: 1, b: 2 };
f2(o);
console.log(o);
// 看上去函数中对于o.a的更新生效,实际上只是值传递
// 对于引用类型,只有全新赋值才算是更新,修改属性不算。
const obj = { x: 1, y: 2 };
obj.x = 20;
// 函数中对于对象参数/引用参数的更新并没有影响
// 深拷贝:值传递
// 浅拷贝:引用传递
模板字面量与标签函数
// 1. 模板字面量:将表达式嵌入字符串中
let a = 1,
b = 2;
let res = a + " + " + b + " = " + (a + b);
console.log(res);
// 模板字面量使用反应“`”
res = `${a} + ${b} = ${a + b}`;
console.log(res);
// 模板字面量的组成:
// 1. 字符串字面量:“+,=”
// 2. 变量或表达式:a,b,(a + b)
let menu = ["首页", "视频", "文章"];
let htmlStr = `<ul>
<li><a href="">${menu[0]}</a></li>
<li><a href="">${menu[1]}</a></li>
<li><a href="">${menu[2]}</a></li>
</ul>`;
console.log(htmlStr);
document.body.insertAdjacentHTML("beforeend", htmlStr);
// document.body.insertAdjacentHTML("beforebegin",htmlStr);
// 2. 标签函数:自定义模板字面量的行为
// 遍历,加样式,动态渲染,放到迭代器中循环
// let hello = name => alert(name);
// hello("朋友");
// hello`朋友`;
// 模板字面量可以当参数使用,函数调用的小括号都可以不写
// 使用自定义函数处理模板字面量,他的参数约定
// 1. 第一个参数:模板字面量中的字符串字面量组成的数组
// 2. 从第二个参数开始,将模板字面量中的变量依次传入
let sum = (strs, a, b) => {
console.log(strs);
console.log(a, b);
};
sum(["+", "=", ""], a, b);
sum`${a} + ${b} =`;
sum = (strs, ...args) => {
console.log(strs);
console.log(args);
};
sum`${a}+${b} = `;
解构赋值
//解构赋值:快速从集合中(数组/对象)解构出独立变量
//1. 数组
let [a, b, c] = [1, 2, 3];
console.log(a, b, c);
[a, b] = [1, 2, 3];
console.log(a, b);
[a, b, c, d] = [1, 2, 3];
console.log(a, b, c, d);
// 还可以使用默认值
[a, b, c, d = "xxx"] = [1, 2, 3];
console.log(a, b, c, d);
// 取部分值,其他值通过rest语法压入数组
[a, b, ...c] = [1, 2, 3, 4, 5];
console.log(a, b, c);
// 想拿到固定位置的值
[, , a, ,] = [1, 2, 3, 4, 5];
console.log(a);
// 两个值交换
let x = 1,
y = 2;
// let t
// t = x;
// x = y;
// y = t;
// console.log(x,y);
[y, x] = [x, y];
console.log(x, y);
// 2. 对象解构
let { id, name } = { id: 10, name: "phone" };
console.log(id, name);
// 属性名与变量名必须对应,顺序无所谓
//当变量名冲突时,可以使用别名
let email = "admin@php.cn";
let { role, email: userEmail } = { role: "user", email: "user@php.cn" };
console.log(userEmail);
console.log(email);
// 3. 参数解构
// 数组传参
let sum = ([a, b]) => a + b;
console.log(sum([10, 20]));
//对象传参
let getUser = ({ name, email }) => [name, email];
console.log(getUser({ email: "tp@php.cn", name: "TP" }));
总结:太好用了。
注意:结构数量不完全匹配的时候,如何接解构的值
[, , a, ,] = [1, 2, 3, 4, 5];
对象字面量的简化
let user = {
userName: 'tp',
userEmail: 'tp@php.cn',
getInfo:function() {
return `${this.userName} ( ${this.userEmail} )`;
}
}
console.log(user.getInfo());
let {userName,userEmail}=user;
console.log(userName,userEmail);
user = {
// 当属性名与同一个作用域中的变量名向同时
// 可以直接使用属性名来引用该变量的值
// userName:userName,
// userEmail:userEmail,
userName,
userEmail,
// getInfo:function() {
// return `${this.userName} ( ${this.userEmail} )`;
// }
// 方法也可以简化
getInfo() {
return `${this.userName} ( ${this.userEmail} )`;
}
// getInfo:()=>
// return `${this.userName} ( ${this.userEmail} )`;
}
console.log("简化后",user.getInfo());
//箭头函数中的this总是指向定义它的作用域(静态作用域/词法作用域),并非调用是的作用域
// js中,只有函数和块才能创建作用域
// user对象不能创建作用域 此时的this指向了上user的作用域/作用域链;
// 全局没有userName,userEmail,所以输出 undefined。
认识bind,call,apply
<button>click</button>
<script>
// function f1() {}
// console.dir(f1);
// bind,call,apply定义在原型上的方法
function hello(name) {
this.name = name;
console.log(this.name);
}
const obj = {
name: "admin",
// hello(name) {
// this.name = name;
// console.log(this.name);
// },
// 代码冗余
};
//经典调用
console.log(hello("hi 你好"));
//bind()不会立即执行,只返回一个函数声明
// 将hello()这个函数绑定到obj对象上,第二个参数是hello的参数
let f = hello.bind(obj, "tp");
console.log(f());
// call / apply 立即执行
//将hello()这个函数绑定到obj对象上,第二个是hello的参数
f = hello.call(obj, "xx");
console.log(f);
//使用apply第二个参数要是数组
f = hello.apply(obj, ["yy"]);
console.log(f);
//bind()应用案例:动态改变this
document.querySelector("button").addEventListener(
"click",
function () {
console.log(this.name);
console.log(this);
document.body.appendChild(document.createElement("p")).innerHTML="欢迎"+this.name;
}.bind({ name: "新对象" })
);
总结:bind不会立即执行,call 和 apply 立即执行 其中apply 要求第二个参数是数组
使用此三个方法的好处是可以动态改变this指向,相应的也会是代码可读性变差。
访问器属性
const product = {
data: [
{ name: "电脑", price: 9000, num: 5 },
{ name: "手机", price: 5000, num: 10 },
{ name: "相机", price: 8000, num: 3 },
],
// getAmounts(){
// return this.data.reduce((t,c)=>(t += c.price*c.num),0)
// },
//访问器属性
//将方法伪造成一个属性
get total(){
return this.data.reduce((t,c)=>(t += c.price*c.num),0)
},
set setPrice(price){
this.data[1].price = price;
}
};
// console.log("总金额:", product.getAmounts());
console.log("总金额:", product.total);
product.setPrice = 7999;
console.log("总金额:", product.data[1]);
总结:感觉很重要的样子。
访问器属性的优先级
// let user = {
// name: "phone",
// get name() {
// return "HUAWEI";
// },
// set name(value) {
// this.name = value;
// },
// };
//访问器属性优先级高于同名的普通属性
// console.log(user.name);
// 怎么解决普通属性与访问器属性重名问题?
let user = {
data:{name},
get name() {
return this.data.name;
},
set name(value) {
this.data.name = value;
},
};
user.name = "HUAWEI";
console.log(user.name);
总结:开发中尽量避免访问器属性与不同属性重名。
流程控制-分支
// let score = 64;
//单分支
// if (score >= 60) {
// console.log("及格");
// }
// let score = 54;
// // 双分支
// if (score >= 60) {
// console.log("及格");
// //默认分支
// } else {
// console.log("补考");
// }
let score = 100;
// 双分支
if (score >= 60 && score < 80) {
console.log("合格");
//默认分支
} else if (score >= 80 && score <= 100) {
console.log("学霸");
} else if (score > 100 || score < 0) {
console.log("非法数据");
} else {
console.log("补考");
}
// switch 来简化多分支
// 只要是区间一定要用true,switch是严格匹配
score = 70;
switch (true) {
case score >= 60 && score < 80:
console.log("合格");
break;
case score >= 80 && score <= 100:
console.log("学霸");
break;
case score > 100 || score < 0:
console.log("非法数据");
break;
default:
console.log("补考");
}
// 一般区间判断不用switch,用if更直观
// switch 一般用做单值判断
let response = 'Success';
switch (response.toLowerCase()) {
case 'fail':
console.log("请求失败");
break;
case 'success':
console.log("请求成功");
break;
default:
console.log("未知错误");
}
// 三元运算符
// 条件 ? true: false;
// if (score >= 60) {
// console.log("及格");
// //默认分支
// } else {
// console.log("补考");
// }
console.log(score >= 60 ? "及格" : "补考");
总结:作为重要内容。三元运算符简化了双分支条件语句。熟练运用可以简化代码,看起来更优雅。
值判断尽量使用switch,如果是区间判断一定要使用true作为条件。