一、定义
标识符(Identifier)就是一个名字,用来对变量、函数、属性、参数进行命名,或者用做某些循环语句中的跳转位置的标记。
//变量
var Identifier = 123;
//属性
(new Object).Identifier = 'test';
//函数及参数
function IdentifierName(Identifier1){};
//跳转标记
Identifier:
for(var i = 0; i < 5; i++){
if(i == 3){
break Identifier;
}
}
二、命名规则
标识符可以是按照下列格式规则组合起来的一或多个字符:
- 第一字符必须是一个字母、下划线(_)、或一个美元符号($);
- 其它字符可以是字母、下划线、美元符号或数字;
//错误示范
6num //开头不能用数字
%sum //开头不能用除(_ $)外特殊符号,如(% + /等)
sum+num //开头中间不能使用除(_ $)外特殊符号,如(% + /等)
javascript是一门区分字母大小写的语言,所以标识符 Identifier 和标识符 identifier 是2个不同的值。
javascript保留了一些标识符为自己所用,不能把关键字、保留字、true、false和null用作标识符。
//关键字
break do instanceof typeof
case else new var
catch finally return void
continue for switch while
debugger function this with
default if throw
delete in try
//保留字
abstract enum int short
boolean export interface static
byte extends long super
char final native synchronized
class float package throws
const goto private transient
debugger implements protected volatile
double import public
//ES6 非严格模式
class enum extends super
const export import
//EW6 严格模式
implements package public
interface private static
let protected yield
javascript允许标识符中出现 Unicode字符全集中的字母和数字(包括中文)。因此,程序员也可以使用非英语语言或数学符号来书写标识符. 但出于可移植性和易于书写的考虑,通常我们不使用扩展的ASCII或Unicode字符
//不推荐
var 测试文字 = 'test';
三、最佳实践
1.通常驼峰格式是标识符命名的首选格式,第一个字母小写,剩下的每个单词的首字母大写
var myMoodToday = 'happy';
2.根据不同的数据类型进行命名
类型 后缀 示例
数组(Array) Arr itemsArr
布尔值(Boolean) Boo isCompleteBoo
浮点数(Float) Fl priceFl
函数(Function) Fn handlerFn
整数(Integer) Int itemCountInt
对象(Object) Obj dIv1Obj
正则表达式(RegExp) Reg emailCheckReg
字符串(String) Str userNameStr
变量(Variant) Var anythingVar
3.一般来说,变量的命名要使用名词,而函数应该是动词+名称的形式,且尽量要在变量名中体现出值的数据类型。比如,命名count、length和size表明数据类型是数字,而命名name、title和message表明数据类型是字符串。用单个字符命名的变量诸如i、j、k通常在循环中使用
var count = 10;
var myName="xiaohuochai";
var found = true;
function getName(){
return 123;
}
而对于函数和方法命名来说,第一个单词应该是动词,下面是一些使用动词常见的约定
can 函数返回一个布尔值
has 函数返回一个布尔值
is 函数返回一个布尔值
get 函数返回一个非布尔值
set 函数用来保存一个值
四、同名标识符的优先级
(1) 流程
在流程之前,必须写一下标识符是啥. 一句话,就是variable object的属性.而这个对象会被不同执行环境来决定. 比如全局环境下的variable object 就是 global, function code 类型的 执行环境,则是 acitvation object ,而 eval code 内的则是 eval code 所在的 calling context 的 variable object . 当然对于eval code稍微有点说法. 参考:ECMA262 Edition5中 所谓eval code 的 direct call 部分.这里就不细说了. 总之所有标识符都是内部对象 variable object的属性.
变量初始化流程是这样的:
词法扫描以后. 把所有标识符找出来,
3种标识符:函数名、形参名、变量名, 是分三个阶段初始化的:
第一步: 初始化函数名,这个过程比较特殊. 因为需要同时创建函数对象, 然后把函数名,作为属性名添加给 variable object ,如果已经存在一个同名的则放弃添加属性名的过程,而直接把创建的函数对象的引用,作为这个属性的值. 如果之前不存在同名属性.则把当前函数名作为属性名,添加给这个variable object .并且对该属性设置 特性集:设置属性无法被delete运算符删除.然后为属性赋值函数对象引用.
第二步: 形参初始化, 同样的,遇到同名的variable object的属性名,放弃添加属性操作. 如果属性名可用,则添加属性,同时,把undefined赋值给这个属性.并设置特性集,使该属性无法被删除.
第三步: 变量初始化,与形参初始化一样.
这里有个例外的初始化过程,发生在 eval code 中.
eval code中 的变量初始化,不会为calling context 的 variable object 添加属性时,设置 [[DontDelete]]特性.所以可以被delete删除.
这就是为什么, 表面上看起来,函数名优先级高于形参,而形参又高于变量名的原因.
(2) 示例
1. 局部变量先使用后声明,不影响外部同名变量
var x = 1; // --> 外部变量x
function fn(){
alert(x); // --> undefined 局部变量x先使用
var x = 2; // 后声明且赋值
}
fn();
alert(x); // --> 1<br>
第一个alert:函数fn内第一句输出的x,x是在第二句才定义的,但在编译过程中发生了变量提升,所以alert undefined.
第二个alert:函数fn内的局部变量x没有影响到外部的变量x。即alert输出是1,不是undefined。
2. 函数名优先级高于形参(变量提升)
function fn(a){
function a () {
console.log('111')
}
alert(a);
}
fn('hello'); // --> function a () { console.log('111') }
可以看到函数名和形参同名都是a,输出的是函数a的函数体(a.toString()),却不是字符串"hello"。
3.形参优先级高于只声明却未赋值的局部变量(变量提升)
function fn(a){
var a;
alert(a);
}
fn('hello'); // --> "hello"
函数fn形参为a,函数内第一句仅声明局部变量a,却并未赋值。从输出结果是"hello"而非undefined可以看出形参a优先级高于仅声明却未赋值的局部变量a。
4.声明且赋值的局部变量优先级高于形参
function fn(a){
var a = 1;
alert(a);
}
fn('hello'); // --> "1"
函数fn形参为a,函数内第一句仅声明局部变量a,赋值为1。从输出结果是"1"而非"hello"可以看出声明且赋值的局部变量a优先级高于形参a。
5.形参优先级高于arguments
function fn(arguments){
alert(arguments);
}
fn('hello'); // --> "hello"<br>
arguments对象可以直接在函数内使用,是语言本身提供的一个 特殊标识符 。
这里刚好将形参声明成与其同名。输出可以看到是"hello"而非"[object Object]",即形参arguments覆盖了语言本身提供的真正的arguments。
五、深入了解函数的作用域链及标识符解析过程
来源:oschina
链接:https://my.oschina.net/u/4364358/blog/3684993