标签:javascript 作用域 闭包 对象 陈小道
笔记来自《Node.js开发指南》BYVoid编著
if (true) {
var somevar = 'value';
}
console.log(somevar);JavaScript的作用域完全是由函数决定的,if、for语句中的花括号不是独立的作用域。
作用域是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,我们称为函数作用域。在函数中引用一个变量时,JavaScript会先搜索当前函数作用域,或者称为“局部作用域”,如果没有找到则搜索其上层作用域,一直到全局作用域。
var scope = 'global';
var f = function() {
console.log(scope); //输出 undefined
var scope = 'f';
}
f();按照作用域搜索顺序,在console.log函数访问scope变量时,JavaScript会先搜索函数f的作用域,恰巧在f作用域里面搜索到scope变量,所以上层作用域中定义的scope就被屏蔽了,但执行到console.log语句时,scope还没被定义,或者说初始化,所以得到的就是undefined值了。var scope = 'top';
var f1 = function() {
console.log(scope);
};
f1(); // 输出 top
var f2 = function() {
var scope = 'f2';
f1();
};
f2(); // 输出 top
在JavaScript中有一种特殊的对象称为全局对象。这个对象在Node.js对应的是global对象,在浏览器中对应的是window对象。
var generateClosure = function() {
var count = 0;
var get = function() {
count++;
return count;
};
return get;
};
var counter = generateClosure();
console.log(counter()); //输出1
console.log(counter()); //输出2
console.log(counter()); //输出3
JavaScript中的对象实际上就是一个由属性组成的关联数组,属性由名称和值组成,值的类型可以是任何数据类型,或者函数和其他对象。注意JavaScript具有函数式编程的特性,所以函数也是一种变量,大多数时候不用与一般的数据类型区分。
var foo = {};
// {}是对象的字面量表示方法,
// 也可以用var foo = new Object()来显式的创建一个对象。
foo.prop_1 = 'bar';
foo.prop_2 = false;
foo.prop_3 = function() {
return 'hello world';
}
console.log(foo.prop_3());
var foo = {};
foo['prop_1'] = 'bar';
foo['prop_2'] = false;
foo['prop_3'] = function() {
return 'hello world';
}
console.log(foo.prop_3());在JavaScript中,使用句点运算符和关联数组引用是等价的,也就是说任何对象(包括this指针)都可以使用这两种模式。使用关联数组的好处是,在我们不知道对象的属性名称的时候,可以用变量来作为关联数组的索引。例如:foo[some_prop]=false;
var foo = {
'prop1': 'bar',
prop2: 'false',
prop3: function() {
return 'hello world';
}
};使用初始化器时,对象属性名称是否加引号是可选的,除非属性名称中有空格或者其他可能造成歧义的字符,否则没有必要使用引号。function User(name, url) {
this.name = name;
this.url = url;
this.display = function() {
console.log(this.name);
}
}
var someuser = new User('ichenxiaodao', 'http://blog.csdn.net/cdztop');
console.log(someuser.name);
console.log(someuser.url);
console.log(someuser.display());
在JavaScript中,上下文对象就是this指针,即被调用函数所处的环境。上下文对象的作用是在一个函数内部引用调用它的对象本身,JavaScript的任何函数都是被某个对象调用的,包括全局对象,所以this指针是一个非常重要的东西。
var someuser = {
name: "ichenxiaodao",
func: function() {
console.log(this.name);
}
};
var foo = {
name: "foobar"
}
someuser.func(); //输出ichenxiaodao
foo.func = someuser.func;
foo.func(); //输出foobar
name = "global";
func = someuser.func;
func(); //输出global
使用不同的引用来调用同一个函数时,this指针永远是这个引用所属的对象。在前面的章节中我们提到了JavaScript的函数作用域是静态的,也就是说一个函数的可见范围是在预编译的语法分析中就可以确定的,而上下文对象则可以看作是静态作用域的补充。
var someuser = {
name: 'ichenxiaodao',
display: function(words) {
console.log(this.name + ' says ' + words);
}
};
var foo = {
name: 'foobar'
};
someuser.display.call(foo, 'hello'); //输出foobar says hello
someuser.display是被调用的函数,它通过call将上下文改变为foo对象,因此在函数体内访问this.name时,实际上访问的是foo.name,因而输出了foobar。
var someuser = {
name: 'ichenxiaodao',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
foo.func = someuser.func;
foo.func(); //输出foobar
// foo.func1使用了bind方法,将someuser作为this指针绑定到someuser.func,
// 调用foo.func1()时,this指针为someuser,所以输出结果是ichenxiaodao。
foo.func1 = someuser.func.bind(someuser);
foo.func1(); //输出ichenxiaodao
func = someuser.func.bind(foo);
func(); //输出foobar
func2 = func;
func2(); //输出foobar
var person = {
name: 'ichenxiaodao',
says: function(act, obj) {
console.log(this.name + ' ' + act + ' ' + obj);
}
};
person.says('loves', 'xixi'); //输出ichenxiaodao loves xixi
xiaodaoLoves = person.says.bind(person, 'loves');
xiaodaoLoves('you'); //输出ichenxiaodao loves you
xiaodaoLoves将this指针绑定到了person,并将第一个参数绑定到loves,之后在调用xiaodaoLoves的时候,只需传入第三个参数。这个特性可以用于创建一个函数的“捷径”,之后我们可以通过这个“捷径”调用,以便在代码多处调用时省略重复输入相同的参数。
在JavaScript语言中,没有类的概念,对象由对象实例化。打个比方来说,基于类的语言中类就像一个模具,对象由这个模具浇注产生,而基于原型的语言中,原型就好像是一件艺术品的原件,我们通过一台100%精确的机器把这个原件复制出很多份。
function Person() {};
Person.prototype.name = 'ichenxiaodao';
Person.prototype.showName = function() {
console.log(this.name);
};
var person = new Person();
person.showName();上面这段代码使用了原型而不是构造函数初始化对象。这样做与直接在构造函数内定义属性有什么不同呢?原型链
JavaScript中有两个特殊的对象:Object与Function,它们都是构造函数,用于生成对象。Object.prototype是所有对象的祖先,Function.prototype是所有函数的原型,包括构造函数。我把JavaScript中的对象分为三类,一类是用户创建的对象,一类是构造函数对象,一类是原型对象。用户创建的对象,即一般意义上用new语句显式构造的对象。构造函数对象指的是普通的构造函数,即通过new调用生成普通对象的函数。原型对象特指构造函数prototype属性指向的对象。这三类对象中每一类都有一个__proto__属性,它指向该对象的原型,从任何对象沿着它开始遍历都可以追溯到Object.prototype。构造函数对象有prototype属性,指向一个原型对象,通过该构造函数创建对象时,被创建对象的__proto__属性将会指向构造函数的prototype属性。原型对象有constructor属性,指向它对应的构造函数。
function Foo() {}
Object.prototype.name = 'My Object';
Foo.prototype.name = 'Bar';
var obj = new Object();
var foo = new Foo();
console.log(obj.name); //输出My Object
console.log(foo.name); //输出Bar
console.log(foo.__proto__.name); //输出Bar
console.log(foo.__proto__.__proto__.name); //输出My Object
console.log(foo.__proto__.constructor.prototype.name); //输出Bar在JavaScript中,继承是依靠一套叫做原型链(prototypechain)的机制实现的。属性继承的本质就是一个对象可以访问到它的原型链上任何一个原型对象的属性。
所有对象类型的变量都是指向对象的引用,两个变量之间赋值传递一个对象并不会对这个对象进行复制,而只是传递引用。
Object.prototype.clone = function() {
var newObj = {};
for (var i in this) {
newObj[i] = this[i];
}
return newObj;
}
var obj = {
name: "ichenxiaodao",
likes: ['node']
};
var newObj = obj.clone();
obj.likes.push('pomelo');
console.log(obj.likes); //输出['node', 'pomelo']
console.log(newObj.likes); //输出['node', 'pomelo']上面的代码是一个对象浅拷贝(shallowcopy)的实现,即只复制基本类型的属性,而共享对象类型的属性。浅拷贝的问题是两个对象共享对象类型的属性,例如上例中likes属性指向的是同一个数组。Object.prototype.clone = function() {
var newObj = {};
for (var i in this) {
if (typeof(this[i]) == 'object' || typeof(this[i]) == 'function') {
newObj[i] = this[i].clone();
} else {
newObj[i] = this[i];
}
}
return newObj;
};
Array.prototype.clone = function() {
var newArray = [];
for (var i = 0; i < this.length; i++) {
if (typeof(this[i]) == 'object' || typeof(this[i]) == 'function') {
newArray[i] = this[i].clone();
} else {
newArray[i] = this[i];
}
}
return newArray;
};
Function.prototype.clone = function() {
var that = this;
var newFunc = function() {
return that.apply(this, arguments);
};
for (var i in this) {
newFunc[i] = this[i];
}
return newFunc;
};
var obj = {
name: 'ichenxiaodao',
likes: ['node'],
display: function() {
console.log(this.name);
}
};
var newObj = obj.clone();
newObj.likes.push('pemole');
console.log(obj.likes); //输出 ['node']
console.log(newObj.likes); //输出 ['node', 'pemole']
console.log(newObj.display == obj.display); // 输出 false上面这个实现看起来很完美,它不仅递归地复制了对象复杂的结构,还实现了函数的深拷贝。但是对于两个对象相互引用的对象,这种方法无能为力。
文档信息
JavaScript【5】高级特性(作用域、闭包、对象),布布扣,bubuko.com
标签:javascript 作用域 闭包 对象 陈小道
原文地址:http://blog.csdn.net/cdztop/article/details/33324543