Javascript的作用域、闭包和对象

一.作用域

作用域是结构化编程语言中的重要概念,它决定了变量的可变范围和生命周期。
和C、Java等语言不同的是,JavaScript不是以{}作为作用域的范围。
例如:

if(true){
	var message = 'hello world';
}
console.log(message);//输出hello world

message的声明和赋值都在if的花括号内,但在if{}外却可以使用。
这是因为JavaScript的作用域完全是由函数来决定的,if、for语句中的花括号不是独立的作用域。

1.1 函数作用域

我们先来看一个例子:

var v1 = 'v1';
var f1 = function(){
	console.log(v1);//输出v1
};
f1();
var f2 = function(){
	var v1 = 'v11';
	console.log(v1);//输出v11
};
f2();

两个函数都使用了v1变量,函数f1在它的函数作用域找不到v1,就搜索上层的作用域,找到v1,输出v1。函数f2在它的函数作用域找到了v1,故就屏蔽了全局作用域中的v1,输出v11。

再来看一个例子:

var v = 'v';
var f = function(){
	console.log(v);//输出undefined
	var v = 'v1';
};
f();

这个例子有点出人意料,因为既没有输出v,也没有输出v1,怎么会出现undefined呢?
这是因为在函数f()中,执行console.log(v)前会先搜索函数作用域中有没有变量v,找到了就屏蔽了全局变量v了,但是此时局部变量v还没被定义或者说初始化,所以输出了undefined。

再看一个例子:

var v = 'v';
var f1 = function(){
	console.log(v);
};
f1();//输出v
var f2 = function(){
    var v = 'v1';
	console.log(v);
};
f2();//输出v

为什么f2()还是输出v呢?这是因为作用域的嵌套关系不是在调用时确定的,而是在定义时确定的。

1.2 全局作用域

满足一下条件的变量属于全局作用域:
(1)在最外层定义的变量
(2)全局对象的属性
(3)任何地方隐式定义的变量(为定义直接赋值的变量)

二.闭包

闭包:由函数(环境)及其封闭的自由变量组成的集合体。
看一个例子:

var generateClosure = function(){
	var count = 0;
	var get = function(){
		count++;
		return count;
	}
	return get;
}
var counter1 = generateClosure();
var counter2 = generateClosure();
console.log(counter1());//输出1
console.log(counter2());//输出1
console.log(counter1());//输出2
console.log(counter1());//输出3
console.log(counter2());//输出2

有点奇怪,count的作用域在generateClosure函数里面,当generateClosure从调用栈返回时count申请的空间就应该被释放,但是为什么没有呢?
这正是所谓闭包的特性。当函数返回它内部定义的一个函数时,就产生了一个闭包,闭包不但包括被返回的函数,还包括这个函数的定义环境。

闭包的用途主要又两个:
(1)实现嵌套的回调函数。
(2)实现私有成员。

三.对象

3.1两种创建js对象的方法

(1)通过字面量来创建,即 var obj={};
再通过obj.attr/obj[attr]/attr:进行赋值和取值。
(2)使用 var obj = new Object()显示地创建一个对象。
eg:

var foo = {
	'prop1':'bar',
	'prop2':'false',
	prop3:function(){
		return 'hello world';
	}
}

3.2 构造函数

如果想像Java和c++语言一样定义对象,怎么实现呢?那就使用js的构造方法。

function User(name,uri){
	this.name = name;
	this.uri = uri;
	this.display = function(){
		console.log(this.name);
	}
}

eg:使用new语句来创建对象。

var someuser = new User('js','https://www.csdn.net');

然后就可以通过someuser来访问这个对象的属性和方法。

3.3 上下文对象

在这里介绍两个适应不同的对象作为上下文来调用函数的方法。
(1)call:func.call(thisArg[,arg1][,arg2][,…])
(2)apply:func.apply(thisArg[,argsArray])
其中,func是函数的引用,thisArg是被调用得分上下文对象,arg1,arg2,argsArray是传入func的参数。
这两个函数的功能是一致的,两者的差别在于接受参数的不同,call以参数表来接受被调用函数的参数,而apply是以数组形式。

举个例子:

var someuser = {
	name:'abc',
	display:function(words){
		console.log(this.name + ' says ' + words);
	}
}

var anotheruser = {
	name:'efg'
}

someuser.display.call(anotheruser,'yyds');

这里会输出:efg says yyds
someuser.display是实际调用的函数,它通过call将上下文改变为anotheruser对象,因此在函数体内访问this.name时实际访问的是anotheruser.name,因此输出了efg。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值