程序中最重要的二个成员: 变量 , 函数
变量
1.字面量
- 字面量:都是一些不可改变的值:
比如:1 2 3 4 5 a b c
字面量都是可以直接使用的,但是我们一般都不会直接使用字面量。
编程原则, 代码中的每个数据,都应该是有意义
- 字面量:都是一些不可改变的值:
2.变量 let
- 变量可以用来保存字面量 可以通过变量对字面量进行描述
- 命名规则
- 1.必须使用英文字母,数字,下划划”_”, $, 只允许用这四种来命名
- 2.可以用中文但不推荐
- 命名风格 有3种
- 1.蛇形: user_name, 多个单词之间用下划线,用在常量
- 2.驼峰: userName, getUserName, 第二个单词起首字母大写,用在变量,函数
- 3.大驼峰: UserName, 帕斯卡, 用在类/构造函数
// 变量声明 let
let num;
// 声明不赋值,默认就是undefined
// 第一次赋值, 初始化
num = 60;
// 第二次赋值, 更新/修改
num = 80;
// 重复声明 let num = 100; 会报错
// let 禁止重复声明,它解决了var一个bug
// 推荐声明的时候进行赋值
let price = 99;
- 常量 const
- 1.就是在代码执行期不允许更新的数据
- 2.声明时必须赋值,名称全部大写
const APP_NAME = "在线商城";
- 在代码中, 我应该用哪个? 首选常量,除非你确定这个数据会发生变化
数据类型
原始类型: 数值(整数和小数), 字符串, 布尔, undefined, null
typeof 操作符返回一个字符串
字符串之间使用加号进行拼装
// 三个最常用的, number, string, bool
// 还有二个特殊的
// undefined null
// let res;
// console.log(typeof res); // 返回undefined
// res = null;
// console.log(typeof res); // 返回 object
// 为什么不返回 null,这是一个历史遗留问题
// 以上5种
// number, string, boolean, undefined, null
// 具有单值,不可在分,它是构成复合类型的基础
引用类型, 数组, 对象, 函数 三种
- (一)数组
const prices = [96, 45, 23, 88];
// 一个变量名,price它是一组相关数据的引用,通过price可以访问到这个集合中任何一个成员
// 多值
console.log(prices);
// 数组使用成员的索引来访问,默认从0开始, [0],[1]...叫偏移量
// console.log(prices[0], prices[1]);
// 数组成员, 可以是任何类型
const arr = [
123,
"adb",
true,
null,
[1, 2, 3, 4, 5],
{ x: 1, y: 2, z: 3 },
function () {
console.log("Hello world");
},
];
(二)对象
// 对象与数组很像,区别在于它的索引是一个字符串或标识符
const item1 = {
// 属性: 变量在对象中叫属性
name: "笔记本电脑",
num: 5,
price: 6699,
};
console.table(item1);
// 使用点语法
console.log(item1.name, item1.num, item1.price);
// 当属性使用了非法标识符时, 必须转为字符串, json就全部采用这种语法
const item2 = {
"my name": "充电宝",
"min-price": 9.9,
};
console.log(item2["my name"], item2["min-price"]);
// 在实际工作中, 对象与数组, 一般是结合着用
const items = [
{ name: "电脑", num: 2, price: 9999 },
{ name: "手机", num: 5, price: 8888 },
{ name: "相机", num: 1, price: 19999 },
];
console.table(items);
- 函数
// 函数推荐使用: 动词 + 名称
function getTotal(name) {
return "hello, " + name;
}
// 使用函数的名称加一对括号
console.log(getTotal("猪老师"));
console.log(typeof getTotal);
// 既然函数是对象,对象就有属性或方法
console.log(getTotal.name);
console.log(getTotal.length);
- (一)数组
函数 function
- 对于完成重复功能,应该考虑用函数进行封装,实现”代码复用”
// function , 函数声明的关键字
// sum1: 函数名称
// (num1, num2) 参数列表, 形式参数,(形参)
// (12, 34): 调用参数,实际参数(实参),与上面的参数列表中的参数一一对应
function sum1(num1, num2) {
let total = 0;
total = num1 + num2;
return total;
}
sum1(5, 22);
- 参数不足, 给函数设置默认参数来解决
参数过多, 使用rest参数,将所有参数压到一个数组中
// 归并参数/剩余参数/不定参数,将多余的参数压到一个数组参数中
// ...: 用在函数的形参中, 就是rest,归并操作,压到一个数组中
function sum3(...args) {
console.log(args);
console.log(
"total =",
args.reduce((p, c) => p + c)
);
}
sum3(10, 20, 30, 40, 50, 60);
// ...: 用在函数的调用参数中, 就是扩展/展开, spread
const arr = [10, 20, 30, 40, 50, 60];
sum3(...arr);
匿名函数
- 第一种是一次性 立即执行函数
(function (a, b) {
console.log(a + b);
})(10, 50);
- 第二种不是一次性 应该使用”函数表达式”
// 表达式: 3+5 就是一个简单的表达式 ,必须返回一个值
let add = function (a, b) {
return a + b;
};
console.log(add(40, 50));
// 使用"箭头函数"来简化"匿名函数表达式"
add = (a, b) => {
return a + b;
};
// 1.如果函数的代码只有一行, 大括号可以不写 return 也不写
add = (a, b) => a + b;
// 2.如果参数只有一个, 参数的括号也可以不要的
add = a => a + 5;
// 3.但是没有参数时, 括号必须写
add = () => 10 + 5;
作用域
块作用域
{
// 块作用域
// if for while 都会用到
let a = 1;
console.log(a); // 块里面可以访问 块外面就不能访问
}
全局作用域 和函数作用域 和 作用域链
<script>
// 函数外面声明: 全局作用域
let username = "猪老师";
function demo1() {
// 函数里面声明: 函数作用域
// 函数里面可以访问到函数外面的变量
// let username = "欧阳老师";
console.log(username);
// username,先在函数内部查询,如果有就直接访问
// 如果没有, 就向上一个作用域查询,一级一级向上,直到查到全局作用域
// 全局有username,返回查询结束, 没有username会报错
// 这么一个查找过程形成的链条就叫做作用域链。
// 作用域链, 查询变量用的
let email = "a@php.cn";
}
demo1();
// console.log(email); // 外面不能访问 函数里面声明的变量
// 作用域是单向的
// 由外向内传递可以, 但是由内向外禁止
</script>
闭包
<script>
// 闭包 要满足二个条件 1. 父子函数 2. 自由变量
// 父函数 parent()
function parent(a) {
// 子函数 f()
function f(b) {
// b 外部参数 c 是私有变量 b,c 都是当前子函数 f 自有变量
let c = 6;
// a : 是子函数 f 的自由变量
return a + b + c;
}
// 返回
return f;
}
// 这个时候,在外部调用parent()会返回 一个函数,此时闭包就产生了
console.log(parent(5));
// 本来 parent()调用结束,应该将空间和参数全部释放
// 但是父函数parent中的一个变量a,被 它的一个子函数正在引用着,所以不能销毁
// const f1 = parent(5);
// console.log(f1);
// console.log(f1(1));
console.log(parent(5)(1));
// 经典应用, 通常与IIFE立即执行函数配合
let counter = (function (n) {
// 子函数
// n: 自由变量, 它不属于子函数
return function () {
// 子函数自有变量只能有二种
// 1. 外部参数, 2. 自己声明的变量
return n++;
};
})(5899);
console.log(counter());
console.log(counter());
</script>
对象字面量的简化
<script>
// 1. 属性的简化
let name = "猪老师";
let user = {
// 对象里面也有一个 name: "猪老师",
// name: "猪老师",
// name: name,
// 如满足以下二个条件,可以使用简化属性 直接写成 name
// 1. 作用域相同: 对象属性与变量处在同一个作用域下面
// 2. 名称相同: 对象属性与变量同名
name,
};
console.log(user.name);
// 2.方法的简化
user = {
name,
// 所谓方法: 就是属性的值是一个函数
// getInfo: function () {
// return "我是 : " + user.name;
// },
// 把上面 getInfo 方法进行简化
// 1.将":function"去掉
// 2.在对象方法中, 尽量不要用箭头函数,除非确定你不会用到this
getInfo() {
return "我是 : " + user.name;
},
};
console.log(user.getInfo());
</script>
this 关键字
<script>
// 在对象中,应该使用一个新变量,这个变量应该永远和当前的对象绑定
// 也就是这个变量,应该是当前对象的一个引用
// 这个变量就是: this 永远 指向当前对象
let mobile = {
name: "iPhone 13",
getInfo() {
// this = mobile; 这行代码应该是你的想像中的伪代码,辅助理解
// this你不需要管理 ,它是由js引擎自动设置的
return this.name;
},
};
console.log(mobile.getInfo());
</script>
模板字符串
模板字符串: 可以使用插值表达式的字符串
<script>
// 传统字符串, 多行和表达式变量的混写非常的麻烦
// es6使用模板字符串, 可以解决 用反引来来声明, 不用引用
let lang = "html\n" + "css\n" + "js";
// 改写成
lang = `html
css
js`;
let a = 10,b = 20;
let res = a + " + " + b + " = " + (a + b);
// 改写成
// 使用 ${...} 插值占位符,可以将变量或表达式嵌入到字符串
res = `${a} + ${b} = ${a + b}`;
</script>
标签函数
- 标签函数: 可以使用”模板字符串”为参数的函数
标签函数,就是在”模板字符串”之前加一个标签/标识符,而这个标签,就是一个函数名
<script>
function total(strings, num, price) {
console.log(strings);
console.log(num, price);
let res = `${strings[0]}${num}${strings[1]}${price}, 总金额: ${num * price} 元`;
console.log(res);
}
total`数量: ${5} 单价: ${90}`;
// 模板字符串参数中的占位符比较多,可以用rest进行归并
// 标签函数的参数是有约定的, 不能乱写, 第一个是字面量数组,从第二起才是内部的占位符参数
// 不能像函数那样加括号调用 sum()
// 两个 ${} 插值占位符 中间会产生一个空字符串
sum`计算多个数之和: ${5}${6}${7}${8}${9}kwgwwkwfe`;
function sum(strings, ...args) {
console.log(strings);
console.log(`${strings[0]} ${args.join()}之和是 ${args.reduce((p, c) => p + c)}`);
}
</script>