3 数据类型

3.1 简介

    JavaScript中的每个值都是属于一种特定的数据类型。JavaScript中一共有以下几种数据类型,详细如下所示:

  • 原始类型:UndefinedNullBooleanNumberStringSymbol
  • 对象:Object

通常将数值、字符串和布尔值三种类型,称之为基本的数据类型,对象类型称之为合成类型(因为一个对象往往是多个基本类型值的合成,可以看作是一个存放各种值的容器),而Undefined和Null一般看作是两个特殊值。

3.2 typeof 运算符

    因为JavaScript的类型系统是松散的,所以需要一种手段来确定任意变量的数据类型,这时可以使用typeof,对一个值使用typeof操作符可以返回以下值

  • undefined 表示值未定义
  • boolean 表示值为布尔值
  • string 表示值为字符串
  • number 表示值为数值
  • object 表示值为对象(不是函数)或null
  • function 表示值为函数
  • symbol 表示值为符号

    JavaScript有三种方法,可以确定一个值是属于什么类型,如下所示:

  • typeof 运算符
  • instanceof 运算符
  • Object.prototype.toString 方法

  这里先介绍常用的typeof方法,示例如下所示:

> typeof 123
"number"
> typeof true
"boolean"
> typeof "1234"
"string"
> typeof function f(){}
"function"
> typeof undefined
"undefined"
> typeof null
"object"
> typeof {}
"object"
> typeof []
"object"

基于typeof这个特性,我们可以用来检查一个没有声明的变量而不报错。如下所示:

> test
VM344:1 Uncaught ReferenceError: test is not defined
    at <anonymous>:1:1
(anonymous) @ VM344:1
> typeof test
"undefined"

在实际编程中,可以利用typeof这一点进行判断,如下所示:

if (test){
  // do something
}

if (typeof test === "undefined"){
  // do something
}

3.3 数值类型

3.3.1 整数和浮点数

    在JavaScript中,所的数字都是64位浮点数形式进行存储,即使整数也是如此。示例如下所示:

> 1.00000 === 1.0
true
> 1 === 1.0
true

JavaScript 语言的底层根本没有整数,所有数字都是浮点类型。而在某些情况下,只有整数才能完成,此时JavaScript会自动把64位的浮点数转换成32位整数,再进行运算,如位运算。

    由于浮点数不是精确的值,所以在涉及到小数的比较和运算是要特别小心。示例如下所示:

> 0.1+0.2 === 0.3
false
> 0.3/0.1
2.9999999999999996
> (0.5-0.3) === (0.3-0.1)
false

3.3.2 数值精度

    根据国际标准IEEE 754,JavaScript浮点数的64个二进制位,最左边到右边的表示方式如下所示:

  • 第1位:符号位,0表示正数,1表示负数
  • 第2~12位:指数部分,共11位
  • 第13~64位:小数部分,共52位

符号位决定一个数的正负,而指数决定了数值的大小,小数部分决定了数值的精度。

3.3.3 数值范围

    根据标准,64位浮点数的指数部分长度是11个二进制位,则指数的范围为0~2047,分出一半负数,则JavaScript能够表示的数值范围为2-1023 (开区间) – 21024,超出这个范围则无法表示。

如果一个数大于等于 $ 2 ^{1024} $ ,那么则会发生正向溢出,此时会返回Infinity

> Math.pow(2,1025)
Infinity

如果一个数小于 $ 2^{1024} $ ,则会发生负向溢出,此时会返回0

> Math.pow(2,-1080)
0

    实际的示例代码如下所示:

var a=2;
for (var i=0;i<100;i++){
	a = a * a
  }
Infinity // 输出结果
var b=0.2;
for (var i=0;i<100;i++){
 	b = b * b
  }
0  // 输出结果

在JavaScript中提供获取数值对象的最大和最小值属性,可以返回表示的具体的最大值和最小值,如下所示:

> Number.MAX_VALUE
1.7976931348623157e+308
> Number.MIN_VALUE
5e-324

3.3.4 数值表示方法

    JavaScript中的数值有多种表示方法,可以用字面形式直接表示,如10(十进制)和0xA2(十六进制),数值也可以用科学计数方式表示,示例如下所示:

> 123e5
12300000
> 123e-2
1.23
> -4.5e5
-450000
> .02e5
2000
> 2e-2
0.02

在科学计数方式中,e可以大写也可以小写

    以JavaScript中,在以下两种情况下,会自动将数值转换为科学计数方式表示,其他情况则采用字面形式直接表示

  • 1.小数点前的位数多于21位
> 123456789012345678901234567890
1.2345678901234568e+29
  • 2.小数点后的零多于5个
> 0.000000123
1.23e-7

3.3.5 数值的进制

    使用字面量直接表示一个数值时,JavaScript对整数提供4种进制的表示方法:二进制、八进制、十进制和十六进制。

  • 二进制:使用前缀0b或0B表示
  • 八进制:使用前缘0o或0O表示,或都仅有0,且只用了0~7的8个数字
  • 十进制:没有前缀
  • 十六进制:使用前缀0x或0X表示

默认情况下,JavaScript内部会自动将二进制、八进制、十六进制转换为十进制,示例如下所示:

> 0b111
7
> 0o76
62
> 0xAF
175
> 098  // 注意与 8 进制的区别
98

3.3.6 特殊数值

3.3.6.1 正零和负零

    在JavaScript中,最左边的一位表示符号位,则意味着任何一个数都对应着一个负值,而0也不例外。在JavaScript中,实际上存在2个0,一个是+0和一个-0。区别就是64位浮点数表示法的符号位不同,但却是等价的。需要注意的事项如下所示

  • 大多数情况下,正零和负零都会被当作正常的0
  • 在+0和-0当作分母时,返回的值是不相等的

示例如下所示:

> 0 === +0
true
> 0 == -0
true
> -0 === +0
true
> (+0).toString()
"0"
> (-0).toString()
"0"
> (1/+0) === (1/-0) //(1/+0)=+Infinity, (1/-0)=-Infinity,所以导致两者不相等
false

3.3.6.2 NaN

    NaN是JavaScript的特殊值,表示非数字(Not a number),一般常出现在将字符串解析成数字出错的情况中或一些数学函数的运算结果中。示例如下所示:

> 1-"a"
NaN
> Math.log(-10)
NaN
> 0/0
NaN
  • NaN不是独立的数据类型,而是一个特殊的数值,它的数据类型依然属于Number,可以使用typeof运算符进行查看。
> typeof(NaN)
"number"
  • NaN不等于任何值,包括其本身
> NaN === NaN
false
  • NaN在布尔运算中被当作false
> Boolean(NaN)
false
  • NaN与任何数(含自身)之间进行运算,得到的都是NaN
> NaN + NaN
NaN
> NaN -32
NaN
> NaN *32
NaN
> NaN+32
NaN

3.3.6.3 Infinity

    Infinity表示无穷,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity

> Math.pow(2,2048)
Infinity
> 28/0
Infinity

    nfinity有正负之分,Infinity表示正的无穷,-Infinity表示负的无穷。

> 1/-0
-Infinity
> -1/-0
Infinity

3.4 字符串类型

    String数据类型表示零或多个字符序列,字符串可以使用双引号(”)、单引号(’)或反引号(`)表示。示例如下所示:

let firstName="Surpas";
let lastName='Lee';
let nickName=`Kevin`;

1.三种引号表示字符串没有任何区别

2.以某种引号作为字符串开头,必须仍然以该种引号作为字符串结尾

  • 1. 字符字面量

    字符串数据类型包含一些字符字面量,用于表示非打印字符或有其他用途的字符,如下所示:

字面量 含义
\n 换行符
\t 制表符
\b 退格
\ 反斜杠(\)
单引号(’)
双引号(”)
` 反引号(`)
  • 2. 字符串的特点

    字符串是不可变的,意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。

  • 3.转换为字符串

    有两种方式可以把一个值转换为字符串。

  • 可以使用几乎所有值都有的 toString() 方法,该方法可以返回当前值的字符串等价物。
let age=28
let ageStr=age.toString()
let flag=true
let flagStr=flag.toString()

console.log(typeof(age),typeof(ageStr),typeof(flag),typeof(flagStr)) // number string boolean string

toString()方法可见于数值、布尔值、对象和字符串值。null 和undefined 值没有toString()方法

    多数情况下,toString()不接收任何参数。不过在对数值调用该方法时,toString()可以接收一个底数参数,即以底数形式输出数值的字符串表示。默认情况下,toString()返回数值的十进制字符串表示。而通过传入参数,可以得到数值的二进制、八进制、十六进制,或者其他任何有效基数的字符串表示

let age=28
console.log(age.toString())    // "28"
console.log(age.toString(2))   // "11100"
console.log(age.toString(8))  // "34"
console.log(age.toString(10))  // "28"
console.log(age.toString(16))  // "1c"
  • 如果在不确定一个值是不是null或undefined,可以使用String()转型函数,它始终会返回表示相应类型值的字符串。String()函数遵循如下规则
    • 如果值有toString()方法,则调用该方法(不传参数)并返回结果。
    • 如果值是null,返回”null”。
    • 如果值是undefined,返回”undefined”
let age=28;
let flag=true;
let person=null;
let name=undefined;

console.log(typeof(String(age)),String(age)); // string "28"
console.log(typeof(String(flag)),String(flag)); // string "true"
console.log(typeof(String(person)),String(person)); // string "null"
console.log(typeof(String(name)),String(name)); // string "undefined"
  • 4.模板字面量

    ECMAScript 6 新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不同,模板字面量
保留换行字符,可以跨行定义字符串。

let mulStr01="Hello Surpass\n Welcome to Shanghai";
let mulStr02=`Hello Surpass
Welcome to Shanghai`;

console.log(mulStr01)
console.log(mulStr02)

    输出结果如下所示:

mulStr01 is: Hello Surpass
 Welcome to Shanghai
mulStr02 is: Hello Surpass
Welcome to Shanghai

模板字面量在定义模板时特别有用,比如下面这个HTML模板:

let pageHTMLTemplate=`
<div>
  <a href="#"/>
    <span>Surpass</span>
</div>
`;

    由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。示例如下所示:

let tempStr01=`Hello Surpass
             Welcome to Shanghai`;
let tempStr02="Hello Surpass\nWelcome to Shanghai"

console.log("tempStr01 length is:",tempStr01.length)
console.log("tempStr01 content is:",tempStr01)
console.log("tempStr02 length is:",tempStr02.length)
console.log("tempStr02 content is:",tempStr02)

    输出结果如下所示:

tempStr01 length is: 46
tempStr01 content is: Hello Surpass
             Welcome to Shanghai
tempStr02 length is: 33
tempStr02 content is: Hello Surpass
Welcome to Shanghai
  • 5.字符串插值

    模板字面量中最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个值。技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript句法表达式,只不过求值后得到的是字符串。

字符串插值通过在 ${} 中使用一个JavaScript表达式实现。

所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript表达式都可以用于插值

在插值表达式中可以调用函数和方法

模板也可以插入自己之前的值

let person="Surpass";
let age=28
// 以前最常用的字符串插值
let personInfoOld="Name is "+ person + " age is " + age;

// 现在可以采用的方式
let personInfoNew=`Name is ${person} age is ${age}`;

// 插值中调用函数和方法
function convertToUpper(word){
    return word.toUpperCase()
}

result=`${convertToUpper("surpass")}`

console.log("old output is: ",personInfoOld);
console.log("new output is: ",personInfoNew);
console.log("after word convert to upper is ",result)

let str="";
function append(){
    str=`${str}surpass`;
    console.log("str is: ",str);
}

for(let i=0;i<3;i++){
    append()
}

    输出结果如下所示:

old output is:  Name is Surpass age is 28
new output is:  Name is Surpass age is 28
after word convert to upper is  SURPASS
str is:  surpass
str is:  surpasssurpass
str is:  surpasssurpasssurpass

3.5 布尔类型

    Boolean类型使用最多的类型之一,有两个字面值:truefalse

  • 1.true和false不同于数值,因此true!=1,false!=0

  • 2.true和false是区分大小写

  • 3.要将其他类型的值转换为布尔值,可以使用Boolean()函数

数据类型 转换为true值 转换为false值
Boolean true false
String 非空字符串 空字符串
Number 非零数值(包括无穷值) 0、NaN
Object 任意对象 null
Undefined N/A(不存在) undefined

    示例如下所示:

let str="Hello,Surpass";
if (str) {
    console.log("str value is true");
}
else{
    console.log("str value is false");
}

// 输出结果
// str value is true

3.6 数组类型

    数组(array)是按顺序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

3.6.1 数组定义

  • 方法一:定义即赋值
var sampleArray=[1,2,3,4]
  • 方法二:先定义再赋值
var sampleArray=[];
sampleArray[0]=1;
sampleArray[1]=2;
sampleArray[2]=3;
sampleArray[3]=4;

    任何类型的数据都可以放入数组中,如下所示:

var sampleArray=[
    {"name":"Surpass"},
    28,
    function(){ return true;},
    ["a","b"]
];

3.6.2 数组本质

    本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object,其特殊特性体现在,其键名是按顺序排列的一个组整数,如下所示:

let sampleArray=["a","b","c","d"];
console.log("sampleArray type is:"+typeof(sampleArray)+" sampleArray key is:"+Object.keys(sampleArray))
// 输出结果:sampleArray type is:object sampleArray key is:0,1,2,3

由于数组成员的键名是固定的(默认总是0、1、2…),因此数组不用为每个元素指定键名。JavaScript规定,对象的键名一律为字符串,所以,数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串。

let sampleArray=["a","b","c","d"];
console.log("sampleArray first element is:",sampleArray[0])
console.log("sampleArray first element is:",sampleArray["0"])
console.log("sampleArray second element is:",sampleArray[1.0000])

输出结果如下所示:

sampleArray first element is: a
sampleArray first element is: a
sampleArray second element is: b

3.6.3 常用方法

3.6.3.1 length

    返回数组成员数量,示例如下所示:

let sampleArray=["a","b","c","d"];
console.log("sampleArray length is:",sampleArray.length)
console.log("temp array length is:",[1,2,3].length)

输出结果如下所示:

sampleArray length is: 4
temp array length is: 3

    只要是数组,就一定有length属性。该属性是一个动态的值。length属性是可写的,其特性如下所示:

  • 如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到length设置的值
  • 如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位
  • 如果人为设置length为不合法的值,则会报错
let sampleArray=["a","b","c","d"];
sampleArray.length=2;
console.log("reduce sampleArray length is:",sampleArray);
sampleArray.length=8;
console.log("add sampleArray length is:",sampleArray);
sampleArray.length="Surpass";

输出结果如下所示:

reduce sampleArray length is: [ 'a', 'b' ]
add sampleArray length is: [ 'a', 'b', <6 empty items> ]
C:\Users\Surpass\Documents\VSCodeProjects\JavaScriptDemo\sample.js:144
sampleArray.length="Surpass"
                  ^

RangeError: Invalid array length
    at Object.<anonymous> (C:\Users\Surpass\Documents\VSCodeProjects\JavaScriptDemo\sample.js:144:19)
    at Module._compile (node:internal/modules/cjs/loader:1099:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47

Node.js v17.8.0

通过这个特性,可以利用length=0来达到清空数组

let sampleArray=["a","b","c","d"];
sampleArray.length=0;
console.log("sampleArray is:",sampleArray); // sampleArray is: []

3.6.3.2 遍历数组

    数组遍历的方法主要有以下几种:

  • 1.for循环
let sampleArray=["a","b","c","d"];
for(let i=0;i<sampleArray.length;i++){
    console.log("sample element is:",sampleArray[i])
}
// 或
let sampleArray=["a","b","c","d"];
for(var i in sampleArray){
    console.log("sampleArray element is:",sampleArray[i])
}
  • 2.while 循环
let sampleArray=["a","b","c","d"];
let index=0;
while(index<sampleArray.length){
    console.log("sampleArray element is:",sampleArray[index]);
    index++
}
//或
let sampleArray=["a","b","c","d"];
let index=sampleArray.length;
while(index--){
    console.log("sampleArray element is:",sampleArray[index]);
}
  • 3.forEach循环
let sampleArray=["a","b","c","d"];
sampleArray.forEach(function (ele){
    console.log("sampleArray element is:",ele)
})

3.6.3.3 push/unshift

  • push:将新元素添加到数组的末尾,并返回新的长度。
  • unshift:方法将新项添加到数组的开头,并返回新的长度。
let sampleArray=["a","b","c","d"];
let pushSampleArrayLength=sampleArray.push("tail");
console.log("push method:new sampleArray element is: "+sampleArray+" new sampleArray length is: "+pushSampleArrayLength);
let unshiftSampleArrayLength=sampleArray.unshift("head");
console.log("unshift method:new sampleArray element is: "+sampleArray+" new sampleArray length is: "+unshiftSampleArrayLength);

输出结果如下所示:

push method:new sampleArray element is: a,b,c,d,tail new sampleArray length is: 5
unshift method:new sampleArray element is: head,a,b,c,d,tail new sampleArray length is: 6

3.6.3.4 pop/shift

  • pop:删除数组的最后一个元素,并返回其删除的值
  • shift:删除数组中的第一个元素,并返回其删除的值
let sampleArray=["a","b","c","d"];
let shiftElement=sampleArray.shift();
console.log("delete first element :"+shiftElement+" sampleArray is:"+sampleArray);
let popElement=sampleArray.pop();
console.log("delete last element is:"+popElement+" sampleArray is:"+sampleArray);

输出结果如下所示:

delete first element :a sampleArray is:b,c,d
delete last element is:d sampleArray is:b,c

3.6.3.5 slice

    返回数组中被选中的元素

let sampleArray=["a","b","c","d"];
console.log("new samplArray is:",sampleArray.slice(1,3)); // new samplArray is: [ 'b', 'c' ]

3.7 对象类型

    对象类型简单来说就是一组键值对(key-value)的集合,是一种无序的复合数据集合。

3.7.1 定义对象

  • 方法一:
let personInfo={
    name:"Surpass",
    age:28,
    location:"Shanghai"
};
// 或
let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
  • 方法二:
let personInfo={};
personInfo.name="Surpass";
personInfo.age=28;
personInfo.location="Shanghai"

3.7.2 常用方法

3.7.2.1 键值读取/赋值

  • 1.读取键,示例代码如下所示:
let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};

console.log("person info is:",Object.keys(personInfo))
// person info is: [ 'name', 'age', 'location' ]
  • 2.值读取和赋值都可以使用以下两种方法
  • 使用点运算符
  • 使用[]运算符
let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
// 方法一:读值
console.log("person name is:",personInfo.name); // person name is: Surpass
// 方法二:读值
console.log("person age is:",personInfo["age"]); // person age is: 28
// 方法一:赋值
personInfo.name="Kevin";
// 方法二:赋值
personInfo["location"]="Wuhan";
console.log("person info is:",personInfo) //person info is: { name: 'Kevin', age: 28, location: 'Wuhan' }

3.7.2.2 键值删除/增加

  • 增加:可使用点运算符或[]增加即可
  • 删除:使用delete
let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
personInfo.from="Wuhan";
personInfo["to"]="Nanjing"
console.log("person info is:",personInfo); // person info is: {name: 'Surpass', age: 28, location: 'Shanghai', from: 'Wuhan', to: 'Nanjing'}
delete personInfo.to;
console.log("person info is:",personInfo); // person info is: {name: 'Surpass', age: 28, location: 'Shanghai', from: 'Wuhan'}

3.7.2.3 in

    in 用于检查键是否存在于某一对象中

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
let nameExist="name" in personInfo;
let fromExist="from" in personInfo;
let toStringExist="toString" in personInfo;
console.log("person info is:",nameExist);  // true
console.log("person info is:",fromExist);  // false
console.log("person info is:",toStringExist); // true

in运算符存在一个问题,其不能识别哪些属性是对象自身的,哪些属性是继承的。就像上面代码中,对象personInfo本身并没有toString属性,但是in运算符会返回true,因为这个属性是继承的。针对这种情况,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
var property="toString"
if (property in personInfo){
    if (personInfo.hasOwnProperty(property)){
        console.log("toString is personInfo owner property");
    }
    else{
        console.log("toString is not personInfo owner property");
    }
}
// toString is not personInfo owner property

3.7.2.4 遍历

    遍历对象,一般常用for … in,如下所示:

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};
for(var item in personInfo){
    console.log("key is:"+item+" value is:"+personInfo[item])
}

输出结果如下所示:

key is:name value is:Surpass
key is:age value is:28
key is:location value is:Shanghai

3.7.2.5 with语句

    with作用是操作同一个对象的多个属性,提供一些书写的方便。其基本语法格式如下所示:

with(对象){
 语句;
}

    示例代码如下所示:

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};

with(personInfo){
   name="Kevin";
   location="Wuhan"
}

console.log("personInfo is",personInfo) // personInfo is { name: 'Kevin', age: 28, location: 'Wuhan' }

    以上代码等同于以下代码:

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};

personInfo.name="Kevin";
personInfo.location="Wuhan";
console.log("personInfo is",personInfo); // personInfo is { name: 'Kevin', age: 28, location: 'Wuhan' }

注意事项:with区块内的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量

let personInfo={
    "name":"Surpass",
    "age":28,
    "location":"Shanghai"
};

with (personInfo){
    name="Kevin";
    from="Wuhan";
    to="Nanjing";
}
console.log("personInfo is",personInfo); // personInfo is { name: 'Kevin', age: 28, location: 'Shanghai' }
console.log("from value is:",from,"to value is:",to); // from value is: Wuhan to value is: Nanjing

3.8 Undefined 类型

    Undefined类型只有一个值,特殊值undefined。当使用var或let声明了变量但没有初始化时,就相当于给变量赋予了undefined值:

var age;
console.log(age==undefined) // true

在对未初始化的变量调用typeof时,返回的结果是undefined,但对未声明的变量调用它时,返回的结果还是undefined

undefined是一个假值

3.9 Null类型

    Null类型同样只有一个值,特殊值null。逻辑上讲,null值表示一个空对象指针,这也是给typeof null会返回object的原因。

    在定义将来要保存对象值的变量时,建议使用null来初始化,不要使用其他值。这样,只要检查这个变量的值是不是null就可以知道这个变量是否在后来被重新赋予了一个对象的引用,示例如下所示:

let person=null
if (person != null) {
    console.log("person 已经申明");
}
else{
    console.log("person 未申明");
}

null是一个假值

原文地址:https://www.jianshu.com/p/c9061b035596

本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注: