浅谈JavaScript的语言特性
来源:互联网 发布:php九九乘法表代码视频 编辑:程序博客网 时间:2024/06/07 05:17
在JavaScript中,作用域、上下文、闭包、函数等算是精华中的精华了。对于初级JSer来说,是进阶必备。对于前端攻城师来说,只有静下心来,理解了这些精华,才能写出优雅的代码。
本文旨在总结容易忘记的重要知识,不会讲基本的概念。如果对基本知识不太熟悉,就去翻下《Javascript权威指南》吧~
参考文章如下(建议读者朋友用chrome看这些文章吧,不然的话会错过很多精彩哦~):
http://dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts/
http://benalman.com/news/2010/11/immediately-invoked-function-expression/
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
语言特性
函数表达式
先看代码段:
var
f =
function
foo(){
return
typeof
foo;
// foo是在内部作用域内有效
};
// foo在外部用于是不可见的
typeof
foo;
// "undefined"
f();
// "function"
json
很多JavaScript开发人员都错误地把JavaScript对象字面量(Object Literals)称为JSON对象(JSON Objects)。 JSON是设计成描述数据交换格式的,它也有自己的语法,这个语法是JavaScript的一个子集。
{ “prop”: “val” } 这样的声明有可能是JavaScript对象字面量,也有可能是JSON字符串,取决于什么上下文使用它。如果是用在string上下文(用单引号或双引 号引住,或者从text文件读取)的话,那它就是JSON字符串,如果是用在对象字面量上下文中,那它就是对象字面量。
// 这是JSON字符串
var
foo =
'{ "prop": "val" }'
;
// 这是对象字面量
var
bar = {
"prop"
:
"val"
};
原型
function
Animal (){
// ...
}
function
cat (){
// ...
}
cat.prototype =
new
Animal();
//这种方式会继承构造函数里面的。
cat.prototype = Animal.prototype;
//这种方式不会继承构造函数里面的。
//还有一个重要的细节需要注意的就是一定要维护自己的原型链,新手总会忘记这个!
cat.prototype.constructor = cat;
function
A() {}
A.prototype = {
x: 10
};
var
a =
new
A();
alert(a.x);
// 10
alert(a.constructor === A);
// false!
function
A() {}
A.prototype = {
constructor: A,
x: 10
};
var
a =
new
A();
alert(a.x);
// 10
alert(a.constructor === A);
// true
function
A() {}
A.prototype.x = 10;
var
a =
new
A();
alert(a.x);
// 10
A.prototype = {
constructor: A,
x: 20
y: 30
};
// 对象a是通过隐式的[[Prototype]]引用从原油的prototype上获取的值
alert(a.x);
// 10
alert(a.y)
// undefined
var
b =
new
A();
// 但新对象是从新原型上获取的值
alert(b.x);
// 20
alert(b.y)
// 30
变量对象
在函数执行上下文中,VO(variable object)是不能直接访问的,此时由活动对象(activation object)扮演VO的角色。 活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象:
function
foo(x, y, z) {
// 声明的函数参数数量arguments (x, y, z)
alert(foo.length);
// 3
// 真正传进来的参数个数(only x, y)
alert(arguments.length);
// 2
// 参数的callee是函数自身
alert(arguments.callee === foo);
// true
}
- 所有函数声明(FunctionDeclaration, FD);
- 所有变量声明(var, VariableDeclaration);
另一个经典例子:
alert(x);
// function
var
x = 10;
alert(x);
// 10
x = 20;
function
x() {};
alert(x);
// 20
a = 10;
alert(window.a);
// 10
alert(
delete
a);
// true
alert(window.a);
// undefined
var
b = 20;
alert(window.b);
// 20
alert(
delete
b);
// false
alert(window.b);
// still 20。b is variable,not property!
var
a = 10;
// 全局上下文中的变量
(
function
() {
var
b = 20;
// function上下文中的局部变量
})();
alert(a);
// 10
alert(b);
// 全局变量 "b" 没有声明.
this
在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型的值,this将设为引用类型值 的base对象(base object),在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值 为null的时候,其值会被隐式转换为全局对象。
(
function
() {
alert(
this
);
// null => global
})();
var
foo = {
bar:
function
() {
alert(
this
);
}
};
foo.bar();
// Reference, OK => foo
(foo.bar)();
// Reference, OK => foo
(foo.bar = foo.bar)();
// global
(
false
|| foo.bar)();
// global
(foo.bar, foo.bar)();
// global
- 第一个例子很明显———明显的引用类型,结果是,this为base对象,即foo。
- 在第二个例子中,组运算符并不适用,想想上面提到的,从引用类型中获得一个对象真正的值的方法,如GetValue。相应的,在组运算的返回中———我们得到仍是一个引用类型。这就是this值为什么再次设为base对象,即foo。
- 第三个例子中,与组运算符不同,赋值运算符调用了GetValue方法。返回的结果是函数对象(但不是引用类型),这意味着this设为null,结果是global对象。
- 第四个和第五个也是一样——逗号运算符和逻辑运算符(OR)调用了GetValue 方法,相应地,我们失去了引用而得到了函数。并再次设为global。
正如我们知道的,局部变量、内部函数、形式参数储存在给定函数的激活对象中。
function
foo() {
function
bar() {
alert(
this
);
// global
}
bar();
// the same as AO.bar()
}
作用域链
通过函构造函数创建的函数的scope属性总是唯一的全局对象。
一个重要的例外,它涉及到通过函数构造函数创建的函数。
var
x = 10;
function
foo() {
var
y = 20;
function
barFD() {
// 函数声明
alert(x);
alert(y);
}
var
barFn = Function(
'alert(x); alert(y);'
);
barFD();
// 10, 20
barFn();
// 10, "y" is not defined
}
foo();
var
x = 10, y = 10;
with
({x: 20}) {
var
x = 30, y = 30;
//这里的 x = 30 覆盖了x = 20;
alert(x);
// 30
alert(y);
// 30
}
alert(x);
// 10
alert(y);
// 30
- x = 10, y = 10;
- 对象{x:20}添加到作用域的前端;
- 在with内部,遇到了var声明,当然什么也没创建,因为在进入上下文时,所有变量已被解析添加;
- 在第二步中,仅修改变量“x”,实际上对象中的“x”现在被解析,并添加到作用域链的最前端,“x”为20,变为30;
- 同样也有变量对象“y”的修改,被解析后其值也相应的由10变为30;
- 此外,在with声明完成后,它的特定对象从作用域链中移除(已改变的变量“x”--30也从那个对象中移除),即作用域链的结构恢复到with得到加强以前的状态。
- 在最后两个alert中,当前变量对象的“x”保持同一,“y”的值现在等于30,在with声明运行中已发生改变。
函数
关于圆括号的问题
让我们看下这个问题:‘ 为何在函数创建后的立即调用中必须用圆括号来包围它?’,答案就是:表达式句子的限制就是这样的。
按照标准,表达式语句不能以一个大括号 { 开始是因为他很难与代码块区分,同样,他也不能以函数关键字开始,因为很难与函数声明进行区分。即,所以,如果我们定义一个立即执行的函数,在其创建后立即按以下方式调用:
function
() {
...
}();
// 即便有名称
function
foo() {
...
}();
// "foo" 是一个函数声明,在进入上下文的时候创建
alert(foo);
// 函数
function
foo(x) {
alert(x);
}(1);
// 这只是一个分组操作符,不是函数调用!
foo(10);
// 这才是一个真正的函数调用,结果是10
(
function
foo(x) {
alert(x);
})(1);
// 这才是调用,不是分组操作符
var
foo = {
bar:
function
(x) {
return
x % 2 != 0 ?
'yes'
:
'no'
;
}(1)
};
alert(foo.bar);
// 'yes'
因此,”关于圆括号”问题完整的答案如下:
当函数不在表达式的位置的时候,分组操作符圆括号是必须的——也就是手工将函数转化成FE。
如果解析器知道它处理的是FE,就没必要用圆括号。
function
testFn() {
var
localVar = 10;
//对于innerFn函数来说,localVar就属于自由变量。
function
innerFn(innerParam) {
alert(innerParam + localVar);
}
return
innerFn;
}
var
z = 10;
function
foo() {
alert(z);
}
foo();
// 10 – 使用静态和动态作用域的时候
(
function
() {
var
z = 20;
foo();
// 10 – 使用静态作用域, 20 – 使用动态作用域
})();
// 将foo作为参数的时候是一样的
(
function
(funArg) {
var
z = 30;
funArg();
// 10 – 静态作用域, 30 – 动态作用域
})(foo);
* 在代码中引用了自由变量
最后:
ECMAScript是一种面向对象语言,支持基于原型的委托式继承。
- 浅谈JavaScript的语言特性
- 浅谈JavaScript的语言特性
- 浅谈JavaScript的语言特性
- 浅谈 JavaScript 编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈 JavaScript 编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈 JavaScript 编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈JavaScript编程语言的编码规范
- 浅谈+JavaScript+编程语言的编码规范
- 典型大数据计算模式与系统
- 动态规划原理解析
- [leetcode]23 Merge k Sorted Lists
- 理解Webkit和chromium:基于chromium内核的webview
- 1.7
- 浅谈JavaScript的语言特性
- shell脚本编程学习笔记(1)
- android动画之属性动画学习
- 关于Android开发中导出jar包后的资源使用问题解决
- SWIFT是什么意思?
- jquery flot 画柱状图
- First集Follow集通俗易懂的讲解加实例
- 5个典型的JavaScript面试题
- Lucene - Overview