一、作用域
作用域规定了变量能够被访问的“范围”,离开这个“范围”变量便不能被访问。作用域分为局部作用域和全局作用域。
-
局部作用域
局部作用域分为函数作用域和块作用域。
1)函数作用域:在函数内部声明的变量。- 只能在函数内部被访问,外部无法直接访问;
- 函数的参数也是函数内部的局部变量;
- 不同函数内部声明的变量无法互相访问;
- 函数执行完毕后,函数内部的变量实际被清空了。
2)块作用域:在JavaScript中使用大括号{}
包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问。 let
声明的变量会产生块作用域,var
不会产生块作用域;const
声明的常量也会产生块作用域;- 不同代码块之间的变量无法互相访问;
- 推荐使用
let
或const
。
-
全局作用域
<script>
标签和.js
文件的最外层就是全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问。- 为window对象动态添加的属性默认也是全局的,不推荐;
- 函数中未使用的任何关键字声明的变量为全局变量,不推荐;
- 尽可能少的声明全局变量,防止全局变量被污染。
-
作用域链
作用域链的本质上是底层的变量查找机制。在函数被执行时,会优先在当前函数作用域中查找变量;如果当前作用域查找不到则会依次逐级查找父级作用域,知道查找到全局作用域。- 嵌套关系的作用域串联起来形成了作用域链;
- 相同作用域链中按着从小到大的规则查找变量;
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域。
-
垃圾回收机制(GC)
垃圾回收机制(Garbage Collection,简称GC):JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收机制自动回收。- 全局变量一般不会回收(关闭页面回收);
- 一般情况下局部变量的值,不用了,会被自动回收。
内存的生命周期:
- 内存分配:当声明变量、函数、对象的时候,系统会自动为他们分配内存;
- 内存使用:即读写内存,也就是使用变量、函数等;
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存。
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏。
1)堆栈空间分配区别:
- 栈:由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面;
- 堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型放到堆里面。
2)两种常见浏览器垃圾回收算法:
-
引用计数法:看一个对象是否有指向它的引用,没有引用了就回收对象。(现代浏览器已不再使用)
- 跟踪记录被引用的次数;
- 如果被引用了一次,那么就记录次数1,多次引用就累加;
- 如果减少一个引用就减一;
- 如果引用次数为0,则释放内存。
缺点:嵌套引用(循环引用),若两个对象互相引用,尽管它们已不再使用,但是他们的引用次数不为0,垃圾回收器不会进行回收,导致内存泄漏。
function fn() { let obj1 = {}; let obj2 = {}; // 互相引用,引用次数都不为0 obj1.a = obj2; obj2.a = obj1; return '引用计数无法回收' } fn();
-
标记清除法:现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
- 将“不再使用的对象”定义为“无法到达的对象”;
- 从根部(JS中指全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象都是还需要使用的;
- 那些无法由根部触发触及到的对象被标记为不再使用,稍后进行回收。
-
闭包(Closure)
一个函数将周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。
也就是:闭包 = 内层函数 + 外层函数的变量
闭包的作用:封闭数据,提供操作,外部也可以访问函数内部的变量。
闭包可能引起的问题:内存泄漏。// 简单的闭包 function outer() { const a = 1; function inner() { console.log(a); // 使用了外部函数的变量,形成了闭包 } inner(); } outer(); // 常见闭包的应用 function outer() { const a = 1; function inner() { console.log(a); } return inner; // 返回内部函数 } const fn = outer(); // fn就是inner函数 fn(); // 1,fn在outer函数外执行,依然可以访问outer函数的变量a,这就是闭包的作用 let i = 0; function count1() { i++; console.log(i); } count1(); // i 是全局变量,不是私有的,容易被修改(污染) // 闭包实现数据私有 function count2() { let i = 0; function add() { i++; console.log(i); // 形成了闭包,i 是私有的,不容易被修改(污染) } } const counter = count2(); // 缺点:有内存泄漏的问题:因为counter是全局作用域中的,往下找可以找到对i的使用,所以 i 不会被回收,造成内存泄漏
-
变量提升
变量在未被声明前被访问会报错,但是变量提升会将变量声明提升到当前作用域的顶部,此时在声明前访问变量的值为undefined
(仅存在于使用var
声明变量)。
let
和const
声明的变量不存在变量提升。变量提升出现在相同作用域中。// 变量提升:变量声明提升到作用域的顶部 // 只提升声明,不提升值 console.log(a); // undefined var a = 1; console.log(a); // 1 function fn() { console.log(a); // undefined var a = 2; console.log(a); // 2 } // let const 不存在变量提升 console.log(b); // 报错:变量未定义 let b = 1; console.log(b); // 1 console.log(c); // 报错:变量未定义 const c = 1; console.log(c); // 1
二、函数进阶
- 函数提升
函数提升是指函数在声明之前即可被调用。// 函数提升,函数声明会被提升到当前作用域的顶部 fn(); // 输出:fn function fn() { console.log('fn'); } // 函数表达式必须先声明和赋值,然后再调用 fn1(); // 报错 var fn1 = function() { console.log('fn1'); }
- 函数参数
1)动态参数
arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
2)剩余参数
剩余参数允许将一个不定数量的参数表示为一个数组。
语法:在最后一个参数前加...
,用于获取多余的参数,这个参数即为剩余参数。// 动态参数 function sum() { // arguments 动态参数只存在于函数内部,是一个伪数组 // arguments 的作用是动态获取函数的实参 // console.log(arguments); let result = 0; for (let i = 0; i < arguments.length; i++) { result += arguments[i]; } console.log(result); } sum(1); // 1 sum(1, 2, 3); // 6 sum(1, 2, 3, 4, 5); // 15 // 剩余参数 function sum2(...args) { // args 是一个真正的数组 // console.log(args); let result = 0; for (let i = 0; i < args.length; i++) { result += args[i]; } console.log(result); } sum2(1); // 1 sum2(1, 2, 3); // 6 sum2(1, 2, 3, 4, 5); // 15 // 剩余参数,至少传两个参数 function sum3(a, b, ...args) { // args 是一个真正的数组 // console.log(args); let result = a + b; for (let i = 0; i < args.length; i++) { result += args[i]; } console.log(result); } sum3(1); // NaN sum3(1, 2); // 3 sum3(1, 2, 3); // 6
展开运算符:
...
展开运算符也是...
,可以展开数组,且不会修改原数组。常在求数组最大值(最小值)、合并数组中使用。// 展开运算符 let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2]; console.log(arr3); // [1, 2, 3, 4, 5, 6]
- 箭头函数
引入箭头函数的目的是更简短的函数写法且不绑定this
,箭头函数的语法比函数表达式更简洁。
箭头函数更使用于那些本来需要匿名函数的地方。
1) 基本语法:() => {函数体}
2)箭头函数参数/** 普通函数 const fn = function () { console.log(111); } 等同于 const fn = () => { console.log(111); } fn(); // 111 **/ // 没有参数时,必须加上括号 const fn1 = () => { console.log(111); } fn1(); // 111 // 只有一个参数时,可以省略小括号 const fn2 = a => { console.log(a); } fn2(222); // 222 // 只有一行代码时,可以省略大括号和return const fn3 = (a, b) => a + b; console.log(fn3(1, 2)); // 3 // 箭头函数可以直接返回对象字面量:将对象字面量包裹在小括号中 const fn4 = (a, b) => ({a, b}); console.log(fn4(1, 2)); // {a: 1, b: 2}
普通函数有arguments
动态参数,但是箭头函数没有arguments
动态参数,有剩余参数。
3)箭头函数中的const sum = (...args) => { let result = 0; for (let i = 0; i < args.length; i++) { result += args[i]; } return result; } console.log(sum(1, 2, 3)); // 6
this
- 箭头函数没有自己的this,箭头函数内的this是继承自外围作用域的。
- 箭头函数的this指向定义时所在的对象,而不是运行时所在的对象。
- 箭头函数的this在定义时绑定,不会被任何方式改变。
- 箭头函数的this指向的是最近一层非箭头函数的this。
const obj = { str: "obj", fn: function () { // 普通函数的this,指向调用它的对象 console.log(this, this.str); // this指向obj,输出obj对象和字符串'obj' } } obj.fn(); // 函数由obj调用,this指向obj const obj1 = { str: "obj1", fn: () => { // 箭头函数继承外围作用域的this console.log(this, this.str); // this指向window,输出window对象和字符串'undefined' } } // fn是箭头函数,本身没有this,继承外围作用域的this, obj1.fn(); // obj1不是函数,没有this,obj1.fn()是在全局作用域调用的,所以this指向window // 箭头函数的this指向定义时所在的对象,而不是运行时所在的对象 const obj2 = { str: "obj2", fn: function () { const fun = () => { console.log(this, this.str); // this指向obj2,输出obj2对象和字符串'obj2' } fun(); } } obj2.fn(); // 箭头函数fun()继承fn()的this,fn()由obj2调用,this指向obj2
三、解构赋值
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。
- 数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
语法:在赋值运算符=
的左侧[]
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。变量的顺序对应数组单元值的位置依次进行赋值操作。如:const [a, b] = [1, 2]
相当于const a = 1, b = 2
。还可以对多维数组进行解构。- 变量值和单元值个数相等时,按顺序依次赋值;
- 变量值少于单元值时,多余的单元值会被忽略;
- 变量值多于单元值时,按顺序赋值完后多余的变量为undefined,这种情况可以使用以下方式解决:
- 使用默认值;
- 使用…rest获取剩余的值(剩余参数)
const arr = [1, 2, 3]; const [a, b, c] = arr; console.log(arr); // [1, 2, 3] console.log(a, b, c); // 1 2 3 const [m, n] = [1, 2]; // 相当于 // const m = 1, n = 2; console.log(m, n); // 1 2 // 交换变量 let x = 1, y = 2; console.log(x, y); // 1 2 // 在以数组开头的表达式中,需要用分号与前面的语句隔开,这里在上一行的末尾加了分号隔开了两个语句 [x, y] = [y, x]; console.log(x, y); // 2 1 // 变量值多于单元值 // 多余的变量值为undefined const [p, q] = [1]; console.log(p, q); // 1 undefined // 可以使用默认值 const [r, s = 2] = [1]; console.log(r, s); // 1 2 // 可以使用...rest获取剩余的值 const [t, ...u] = [1, 2, 3]; console.log(t, u); // 1 [2, 3] // 按需导入赋值 const [d, e, , f] = [1, 2, 3, 4]; console.log(d, e, f); // 1 2 4 // 多维数组解构 const [g, [h, i]] = [1, [2, 3]]; console.log(g, h, i); // 1 2 3
- 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法。
语法:在赋值运算符=
的左侧{}
用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量,对象属性的值将被赋值给与属性名相同的变量。可以多级对象解构。- 解构的变量名不要和外面的变量名冲突否则报错。
- 对象中找不到与变量名一致的属性时变量值为
undefined
。 - 解构时可以变量名可以重新命名,语法:
{与属性名相同的变量名: 新变量名}
。
// 对象解构 const obj = {name: '张三', age: 18 }; const {name, age} = obj; console.log(name, age); // 张三 18 // 给变量名重新命名 const obj1 = {uname: '张三', gender: '男' }; const {uname: myName, gender: myGender} = obj1; // 对象解构的变量名可以重新命名, console.log(myName, myGender); // 张三 男 // 多级对象解构 const obj2 = { class: {className: '三年二班', teacher: '李老师'}, students: ['张三', '李四'] }; const { class: {className, teacher}, students: [student1, student2] } = obj2; console.log(className, teacher, student1, student2); // 三年二班 李老师 张三 李四
四、forEach和filter方法
-
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数,没有返回值。
语法:被遍历的数组.forEach(function(当前数组元素, 当前元素索引号){函数体})
。- forEach主要是遍历数组;
- 参数的当前元素是必须要写的,索引号是可选的。
const arr1 = [0, 10, 20, 30, 40]; arr1.forEach(function(item){ console.log(item); // 依次输出数组中的元素 })
-
filter()
方法用于筛选数组中符合条件的元素,并返回新数组。
语法:被遍历数组.filter(funtion(currentValue, index){ return 筛选条件 })
。const arr = [0, 10, 20, 30, 40]; const res = arr.filter(function(item) { return item > 20; // 返回数组中大于20的元素 }) console.log(res); // 输出:[30, 40]
五、创建对象的三种方式
- 利用对象字面量创建:
const obj = {name: "张三"}
- 使用
new Object()
:const obj = new Object(); obj.name = "张三"; console.log(typeof obj, obj); // 输出:object {name: "张三"} const obj1 = new Object({name: "张三"}); console.log(typeof obj1, obj1); // 输出:object {name: "张三"}
- 利用构造函数
六、构造函数
构造函数是一种特殊的函数,主要用来初始化对象。可以快速创建多个类似的对象。
语法:大写字母开头的函数,使用new
关键字实例化。
- 实例化构造函数时若没有参数可以省略括号();
- 构造函数内部无需写
return
(写了也无效),返回值即为新创建的对象; new Object()、new Date()
也是实例化构造函数。
// 创建构造函数,构造函数名以大写字母开头
function User(name, age) {
this.name = name;
this.age = age;
}
// 创建新对象(实例化):使用new
const user1 = new User('张三', 18)
console.log(user1); // 输出:User {name: '张三', age: 18}
实例成员:
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)。构造函数创建的实例对象彼此独立,互不影响。
静态成员:
构造函数的属性和方法被称为静态成员(静态属性和静态方法)。静态成员只能构造函数来访问。静态方法中的this
指向构造函数。
七、内置构造函数
在字符串、数值、布尔等基本类型中也有专门的构造函数,这些我们称为包装类型。
- 引用类型:
Object、Array、RegExp、Date
等。 - 包装类型:
String、Number、Boolean
等。
- Object
Object的三个常用静态方法(只有Object可以调用)。Object.keys(对象)
:获取对象中所有属性名;Object.values(对象)
:获取对象中所有属性值;Object.assign(目标对象, 被拷贝的对象)
:拷贝对象,常用于给对象添加属性。
const obj = {name: '张三', age: 18}; // 获取所有属性名 console.log(Object.keys(obj)); // 输出:['name', 'age'] // 获取所有属性值 console.log(Object.values(obj)); // 输出:['张三', 18] // 拷贝对象 const o = {}; Object.assign(o, obj); console.log(o); // 输出:{name: '张三', age: 18} Object.assign(o, {gender: '男'}); console.log(o); // 输出:{name: '张三', age: 18, gender: '男'}
- Array
1)常见实例方法(通过数组实例对象调用):forEach
:遍历数组,不返回数组,经常用于查找遍历数组元素;filter
:过滤数组,返回新数组,用于筛选满足条件的数组元素;map
:迭代数组,返回新数组,返回经过处理之后的新数组;reduce
:累计器,返回累计处理的结果,经常用于求和。语法:数组.reduce(function(上一次值, 当前值){}, 起始值)
。
reduce使用:
如果没有起始值,则上一次值以数组的第一个元素开始;
每一次循环,把返回值作为下一次循环的上一次值;
如果有起始值,则起始值作为第一次循环的上一次值。
2)数组其他方法const arr = [10, 15, 20, 25, 30]; // 无初始值 const total = arr.reduce(function(prev, current){ return prev + current; }) console.log(total); // 输出:100 // 有初始值 const total1 = arr.reduce(function(prev, current){ return prev + current; }, 10) console.log(total1); // 输出:110 // 箭头函数写法 const total2 = arr.reduce((prev,current) => prev + current, 10); console.log(total2); // 输出:110 // 使用reduce将对象数组进行分组合计 // 计算总分 const gradeArr = [ {student: '张三', score: 75}, {student: '李四', score: 80}, {student: '王五', score: 85}, {student: '张三', score: 75}, {student: '李四', score: 90}, {student: '王五', score: 80}, {student: '张三', score: 90}, {student: '李四', score: 80}, {student: '王五', score: 90}, ]; const scoreArr = gradeArr.reduce((prev, current) => { // 定义一个变量用于存储当前学生姓名 const stu = current.student; if (prev[stu]) { prev[stu] += current.score; // 如果当前学生已存在,则将其值累加 } else { prev[stu] = current.score; // 如果当前学生不存在,则添加新键并赋值 } return prev; },{}) console.log(scoreArr); // 输出:{张三: 240, 李四: 250, 王五: 255}
数组.join(拼接符号)
:将数组元素拼接为字符串,返回字符串(不改变原数组),拼接符号为空时默认为逗号;数组.find(回调函数)
:查找数组中是否存在某个符号,返回数组中符合条件的第一个元素,没有则返回undefined
;数组.every(回调函数)
:检测数组中是否所有元素都符合指定条件,若都符合则返回true
,否则返回false
;数组.some(回调函数)
:检测数组中是否有元素符合指定条件,若有则返回true
,否则返回false
;数组1.concat(数组2)
:合并两个数组,返回新数组;数组.sort()
:对原数组元素进行排序,返回排序后的数组,会改变原数组,默认升序;数组.splice(起始索引, 要删除的个数, 要插入的元素)
:删除或替换原数组(改变原数组),返回值为被删除的元素数组;数组.reverse()
:反转数组;数组.findIndex(回调函数)
:查找元素索引值,返回满足条件的第一个元素的索引值,若没有找到则返回-1
。
const arr1 = ['a', 'b', 'c']; const arr2 = [1, 2, 3]; // join 拼接 console.log(arr1.join(), arr2.join('')); // 输出:a,b,c 123 // find 查找符合条件的元素 console.log(arr1.find((item) => item === 'd')); // 输出:undefined console.log(arr2.find((item) => item > 1)); // 输出:2 // every,some 检测数组中元素是否符合某个条件 console.log(arr2.every((item) => item > 0)); // 输出:true console.log(arr2.every((item) => item > 1)); // 输出:false console.log(arr2.some((item) => item > 2)); // 输出:true console.log(arr2.some((item) => item > 3)); // 输出:false // concat 合并数组 console.log(arr1.concat(arr2)); // 输出:['a', 'b', 'c', 1, 2, 3] // sort 数组排序,会改变原数组 console.log(arr2.sort((a, b) => b - a)); // 输出:[3, 2, 1] console.log(arr2); // 改变了原数组,输出:[3, 2, 1] console.log(arr2.sort()); // 默认升序,输出:[1, 2, 3] // splice 删除或替换原数组元素 const arr3 = [0, 1, 2, 3, 4] // 从索引为2的位置开始删除后面所有元素(包括索引为2的元素) // console.log(arr3.splice(2), arr3); // 输出:[2, 3, 4] [0, 1] // 从索引为1的位置开始删除2个元素(包括索引为1的元素) // console.log(arr3.splice(1, 2), arr3); // 输出:[1, 2] [0, 3, 4] // 从索引为1的位置开始删除2个元素(包括索引为1的元素),并将后面几个参数插入数组 // console.log(arr3.splice(1, 2, 6), arr3); // 输出:[1, 2] [0, 6, 3, 4] // console.log(arr3.splice(1, 2, 6, '7'), arr3); // 输出:[1, 2] [0, 6, '7', 3, 4] // 若第二个参数为0,则表示不删除元素 console.log(arr3.splice(1, 0, 6, '7'), arr3); // 输出:[0, 6, '7', 1, 2, 3, 4] // reverse 反转数组 console.log(arr1.reverse()); // 输出:['c', 'b', 'a'] // 查找索引值 console.log(arr2.findIndex(item => item > 1)); // 输出:1 console.log(arr2.findIndex(item => item > 3)); // 输出:-1
- String
常见方法:字符串.length
:获取字符串长度;字符串.split(分割符, 数组最大长度)
:将字符串分割成数组,第二个参数可选(不改变原字符);字符串.substring(截取起始索引, 截取结束索引号)
:截取指定长度的字符串,第二个参数可选,返回结果不包括结束索引号对应的字符;字符串.startsWith(检测字符串, 检测位置索引号)
:检测字符串指定位置是否以某个字符串开头,第二个参数可选,检测成功返回true
,否则返回false
;字符串.includes(检测字符串, 检测位置索引号)
:检测某个字符串是否包含在另一个字符串中,包含返回true
,否则返回false
;字符串.toUpperCase()
:将字母转为大写字母;字符串.toLowerCase()
:将字母转为小写字母;字符串.indexOf(被检测字符)
:检测字符串中是否存在某字符;字符串.endsWith(检测字符串, 截止检测位置索引号)
:与startsWith用法类似
;字符串.replace(字符串或正则表达式, 字符串或函数)
:替换字符串;字符串.match(搜索值)
:匹配字符串,返回一个包含信息的数组,没有匹配成功返回null
。
const str = 'hello world, hi'; // length 获取字符串长度 console.log(str.length); // 输出15 // split 分割字符串 console.log(str.split(' ')); // 以空格分割,输出:['hello', 'world!', 'hi!'] console.log(str.split(',')); // 以逗号分割,输出:['hello world', ' hi'] // 若分割符为空字符串,则会以单个字符形式返回,包括空格 console.log(str.split('')); // 输出:['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', ',', ' ', 'h', 'i'] // 若分割符在字符串开头或结尾匹配成功,则返回的数组中第一个或最后一个元素对应一个空字符串 console.log('|-a|'.split('|')); // 输出:['', '-a', ''] // 指定返回数组元素的最大个数 console.log(str.split(',', 1)); // 以逗号分割并数组中最多只有一个元素,输出:['hello world'] console.log(str.split('', 5)); // 返回字符串中前五个字符,输出:['h', 'e', 'l', 'l', 'o'] // substring 截取字符串,返回结果不包括结束索引位置的字符 console.log(str.substring(6)); // 截取索引号从第六到最后所有的字符,输出:world, hi console.log(str.substring(6, 11)); // 截取索引号从第六到第十的字符,输出:world // startsWith 检测字符串指定位置是否是以某字符串开头,第二个参数表示开始索引号 console.log(str.startsWith('w'), str.startsWith('h')); // 不指定位置时默认从第一个字符开始匹配,输出:false, true console.log(str.startsWith('w', 6), str.startsWith('wo', 6)); // 从索引号为6的位置开始匹配,输出:true, true // includes 检测字符串是否包含某个指定字符串 console.log(str.includes('world')); // 输出:true // 第二个参数表示从指定索引位置往后匹配 console.log(str.includes('hello', 6), str.includes('world', 6)); // 输出:false, true // toUpperCase 将字母转为大写 console.log(str.toUpperCase()); // 输出:HELLO WORLD, HI // toLowerCase 将字母转为小写 console.log('ABC'.toLowerCase()); // 输出:abc // indexOf 检测是否包含某字符 console.log(str.indexOf('e'), str.indexOf('wo'), str.indexOf('a')); // 输出:1 6 -1 // endsWith 检测是否以某字符结尾,第二个参数表示结束索引号(不包括结束索引号对应的字符) console.log(str.endsWith('i'), str.endsWith('w'), str.endsWith('w', 7)); // 输出:true false true // replace 替换字符串,支持正则匹配 console.log('abc'.replace('b', 'd')); // 输出:adc // match 用于查找字符串,支持正则匹配 console.log('abc'.match('b'), 'abc'.match('d')); // 输出:['b', index: 1, input: 'abc', groups: undefined] null
- Number
number.toFixed(小数位的位数)
:设置保留小数位的位数,四舍五入(整数后面添0)。