JavaScript:作用域和闭包

参数传递

  1. 基本类型有undefined null number string boolean 这几种基本类型是按值访问的,就是说存放的是变量中实际的值。
  2. object类型 存放的不是值本身,是指向对象的一个指针。
var num1 = 5;
var num2 = num1;
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); // "Nicholas"

这里写图片描述
这里写图片描述


// 函数的参数与传递  js都是值传递(对比C语言值传递和引用传递)
/**
 * C语言写法(传值)
 * void addTen(int *num)
 * {
		*num = *num + 10;
   }
   
   传引用
   void addTen(int &num)
   {
	   num = num + 10;
   }
 */
function addTen(num){
	num += 10;
	return num;
}
var count = 20;
var result = addTen(count);
console.log(count); // 20 没有变化
console.log(result);// 30


function setName(obj){
	obj.name = "kitty";
}
var obj3 = new Object();
setName(obj3);
console.log(obj3.name);  // kitty



function setName(obj) {
	obj.name = "Nicholas";
	obj = new Object(); //这个obj还是参数的obj 只是它指向的内容是setName函数内部的一个局部对象了
	obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name); //"Nicholas"
console.log("====================");


// 再测试一次
var object = {"name":"xiaoming","sex":"男"}; 
function setName(obj) {
	obj.name = "Nicholas";
	obj = object;  // 指向了全局变量 object
	console.log(obj); // {"name":"xiaoming","sex":"男"};
	obj.age = "23";
}
var person2 = new Object();
setName(person2);
console.log(person2.name); //"Nicholas"
console.log(object); //{name: "xiaoming", sex: "男", age: "23"}

对最后一个函数测试画一个简图
这里写图片描述
当执行obj.name = “Nicholas”;的时候 obj和person指向的是同一个东西,等执行下一句话的时候
这里写图片描述
此时,person和obj指向的不是一个东西了,所以和person没关系了,因此会出现上面的结果。

作用域

  1. ES5 及其之前只有函数内的变量和函数外的变量,没有块作用域!!!!外边的不可以访问里边的,里边的可以访问外边的。
  2. ES6 出来个let,会形成块作用域,比如一个大括号里边有效。
if (true) {
	var color = "blue";
}
alert(color); //"blue"
for (var i=0; i < 10; i++){
	doSomething(i);
}
alert(i); //10
// 上面的两个局部变量不会和C,Java 一样内存释放,而是保留下来了!!


var color = "blue";
function changeColor(){
	// 局部变量没有color 就往外找
	console.log(arguments);
	if(color == "blue"){
		color = "red";
	}else{
		color = "blue";
	}
}

changeColor();
console.log("Color is now " + color); // Color is now red


function buildUrl() {
	var qs = "?debug=true";
	with(location){ // 用with添加作用域链
		var url = href + qs; // 
	}
	/*在此,with 语句接收的是location 对象,因此其变量对象中就包含了location 对象的所有属性和方法,而这个变量对象被添加到了作用域链的前端。buildUrl()函数中定义了一个变量qs。当在with 语句中引用变量href 时(实际引用的是location.href),可以在当前执行环境的变量对象中找到。*/
	return url;	
}


var color = "blue";
function changeColor(){
	var anotherColor = "red";
	function swapColors(){
		var tempColor = anotherColor;
		anotherColor = color;
		color = tempColor;
		// 这里可以访问color、anotherColor 和tempColor
	}
	// 这里可以访问color 和anotherColor,但不能访问tempColor
	swapColors();
}
// 这里只能访问color
changeColor();

这里写图片描述

函数和闭包

  1. 闭包就是在函数内部定义了其他函数
  2. 匿名函数就是没有函数名的函数
  3. 函数名是指向函数体的一个指针变量,当然也可以赋值给别的变量
  4. 闭包只能取得包含函数中任何变量的最后一个值
function printInfo(){
	console.log("Hello World");
}
printInfo();

// 定义一个变量指向该函数体  类似C++的函数指针
var a = printInfo;
a();

// 定义成匿名函数的形式
var b = function(){
	console.log("Hello World");
}
b();



/**
 * JavaScript高级程序设计:
 * 作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最
   后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰地说
   明这个问题。
 */
function createFunction(){
	var result = new Array();
	for(var i = 0; i < 10;i++){
		result[i] = function(){
			return i;
		}
	}
	return result;
}

var res = createFunction();
for(var i = 0; i < res.length; i++){
	console.log(res[i]());  // 都是10
}

/**
 * 这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置0 的函数
   返回0,位置1 的函数返回1,以此类推。但实际上,每个函数都返回10。因为每个函数的作用域链中
   都保存着createFunctions() 函数的活动对象, 所以它们引用的都是同一个变量i 。当
   createFunctions()函数返回后,变量i 的值是10,此时每个函数都引用着保存变量i 的同一个变量
   对象,所以在每个函数内部i 的值都是10。
 */

这句话翻译成大白话就是:匿名函数里面没有局部变量i,所以向上找,当找到i的时候,for循环已经结束了,i已经变成10了,而且i只有一个,找到的是同一个i,所以解决办法就是在匿名函数里面引入变量i,这样把每一个i都保存起来防止都指向一个i。解决办法如下:

function createFunction(){
	var result = new Array();
	for(var i = 0; i < 10;i++){
		result[i] = (function(i){
			return function(){
				return i;
			};
		})(i)
	}
	return result;
}

var res = createFunction();
for(var i = 0; i < res.length; i++){
	console.log(res[i]());  
}

改成这样的话,return一个匿名函数,这个匿名函数找不到就往上找,正好参数就是i,所以每一个return 就找他爸爸的i,result[i]就对应十个不同的i。
下面HTML和js也是这个道理,困扰很长时间

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
</head>
<body>
	<button>1</button>
	<button>2</button>
	<button>3</button>
	<button>4</button>
	<button>5</button>
	<button>6</button>
	<button>7</button>
	<button>8</button>
</body>
	<script type="text/javascript">
		var btns = document.getElementsByTagName("button");
		for(var i = 0; i < btns.length; i++){
			btns[i].onclick = function(){
				alert(i); // 结果都是8
			}
		}
	</script>
</html>

需要改成这个样子:

var btns = document.getElementsByTagName("button");
for(var i = 0; i < btns.length; i++){
	btns[i].onclick = (function(i){
		return function(){
			alert(i);
		}
	})(i);
}

有人就会问了,为什么不像下面这么改,为什么非得return一个function?

for(var i = 0; i < btns.length; i++){
	btns[i].onclick = (function(i){
		alert(i);
	})(i);
}
// 如果这样的话,不等你点击按钮,就会立刻执行
// (function(i){})(i)//这个称为即时函数,定义完成就直接执行,但是click事件应该返回一个函数,这个返回啥了?就一个单单的alert,所以逻辑上有问题。

闭包和this

this对象是在运行时基于函数执行环境绑定的,在全局函数中,this=window,在函数被作为某个对象的方法调用时,this等于这个对象。但是匿名函数的执行环境是全局性的

var name = "The Window";
var object = {
	name: "My Object",
	getNameFunc: function () {
		console.log(this === object); // true
		console.log(this === window);  // false
		return function () {
			console.log(this === window); // true
			console.log(this == object); // false
			return this.name;
		};
	}
};
console.log(object.getNameFunc()()); //"The Window"(在非严格模式下)

猜你喜欢

转载自blog.csdn.net/tsfx051435adsl/article/details/81736063