JavaScript-3

12,闭包

(1)作用域链:

l  javascript不存在大括号级的作用域,但具有函数作用域。

l  在函数内定义的变量在函数外是不可见的。但如果该变量是在某个代码块中定义的(如在某个if或for语句中),它在代码块外是可见的。

示例:

	var a = 1;
	function f(){
		var b = 2;
		function n(){
			var c = 3;
			alert(a);	//output	1
			alert(b);	//output	2
			alert(c);	//output	3
		}
		return n();
	}
	f();
说明:函数n( ) 可以访问的变量可以是自身的作用域,也可以是其“父级”的作用域。这就形成了一条作用域链。

(2)闭包

定义:指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。

示例:

var b;
	function a(){
		var a = "a";
		b = function(){
			return a + "b";
		}
		return a;
	}
	
	//测试
	alert(a());		//output	a
	alert(b());		//output	ab

说明:

l  在a ( ) 函数中定义了 b ( ) 函数,所以b ( ) 函数可以访问a ( ) 函数的作用域。

l  将 b ( ) 函数升级到全局函数,但依然保留可以对a ( ) 函数作用域的访问权。

第二个alert中,调用了b函数,而b函数是在a函数中定义的,在a函数中没有通过var定义b,这时b是一个全局变量,所以这里的b相当于一个全局函数,不是a的私有函数,但是在b函数中却访问了a函数中的局部变量a,形成闭包。


(3)循环中的闭包:

function f(){
		var a = [];
		var i;
		for(i = 0; i < 3; i++){//循环三次以后i的值加到3,至此不满足条件,跳出循环
			a[i] = function(){
				return i;
			}
		}
		return a;
	}
	
	var fun = f(); 	//通过调用函数f()返回a数组,a数组指向三个函数
	
	alert(fun[0]());		//output	3		fun指向a数组
	alert(fun[1]());		//output	3
	alert(fun[2]());		//output	3
说明:

这个例子中,乍一看,应该是打印0,1,2,但是结果却是3,3,3,为什么呢?在函数f中,确实是a[0]返回0,a[1]返回1,a[2]返回2,但是这只是在初始化a数组时的结果,当fun接收通过调用函数f返回的数组a时,这时a数组函数返回的i,这里的i只是一个引用,而i退出循环时已经加到3,所以这里的i全是3,

修正:

function f(){
	var a = [];
	var i;
	for(i = 0; i < 3; i++){
		a[i] = (function(x){
			return x;
		})(i); //自调函数的参数
	}
	return a;
}
	
var fun = f();
	
alert(fun[0]);		//output	0
alert(fun[1]);		//output	1
alert(fun[2]);		//output	2
说明:

通过自调函数的形式定义函数数组a,当fun[0]执行的时候,调用自调函数,起始是一个匿名函数的引用,因为索引是0,所以自调函数的参数传入的是0,所以return了0,结果就是0,1,2。

13,定义对象的三种方式:

(1)三种方法:

第一种构造法:new Object

var a = new Object();

第二种构造法:直接量方法

var b = {

    x: 1,

    y: 2

}

第三种构造法:定义类型

function Point(x,y){

    this.x= x;

    this.y= y;

}

var p = new Point(1,2);

(2)访问对象的属性:

中括号表示法:hero[‘name’]

点号表示法:hero.name

如果访问的属性不存在,将会返回undefined。

(3)访问对象的方法:

方法名后加一对括号:hero.say()

像访问属性一样访问方法:hero[‘say’]()


示例:

	//创建一个空对象
	var hero = {};
	
	//为hero对象增加属性和方法
	hero.name = "javascript";
	hero.value = "helloworld";
	hero.sayName = function(){return "hello " + hero.name;};
	
	//测试
	alert(hero.name);		//output	javascript
	alert(hero.sayName());		//output	hello javascript
	
	//删除hero对象的name属性
	delete hero.name;
	
	//测试
	alert(hero.sayName());		//output	hello undefined

14,this的使用

this实际上引用的是‘这个对象’或者‘当前对象’。

在jQuery中需要深刻理解。

 

 

15,内建对象

内建对象大致上可以分为三个组:

l  数据封装类对象 —— 包括Object、Array、Boolean、Number和String。这些对象代表着javascript中不同的数据类型,并且都拥有各自不同的typeof返回值,以及undefined和null状态。

l  工具类对象 —— 包括Math、Date、RegExp等用于提供遍历的对象。

l  错误类对象 —— 包括一般性错误对象以及其他各种更特殊的错误类对象。它们可以在某些异常发生时帮助我们纠正程序工作状态。

(1)Object

Object对象是javascript中所有对象的父级对象。所有的对象都继承与Object。

创建空的Object对象:

var obj = {};

var obj = new Object();

(2)Array

用于在单个变量中存储多个值。

创建一个Array对象:

var arr = new Array();

主要方法:reverse反转,join(…)以指定的字符将数组分割并形成字符串

(3)String

String对象与基本字符串之间的区别:通过typeof测试二者的数据类型发现:基本字符串的类型是String,而String对象的类型是Object。

16,原形:prototype

指的是一个函数对象的一个属性。可以利用原形添加方法与属性,利用自身属性重写原形属性,扩展内建对象。

示例:我们新创建一个Hero对象。

function Hero(name, color){

    this.name= name;

    this.color= color;

    this.whatareyou= function(){

       return"I am a " + this.color + " " + this.name;

    }

}

var hero = newHero("javascript","red");

alert(hero.whatareyou());   //output   Iam a red javascript

利用原形prototype给Hero对象添加方法与属性:

Hero.prototype.price = 100;

Hero.prototype.rating = 3;

Hero.prototype.getInfo = function(){

    return"Rating: " + this.rating + " , Price: " + this.price;

}

   

alert(hero.getInfo());      //output   Rating:3 , Price: 100

上面的方式也可以这么做:

Hero.prototype = {

    price: 100,

    rating: 3,

    getInfo: function(){

       return "Rating: " + this.rating+ " , Price: " + this.price;

    }

};


利用自身属性重写原形属性:

如果对象自身属性与原型属性同名该怎么办呢?答案是自身属性的优先级高于原形属性。

function Hero(){
	this.name = "jscript";
}
Hero.prototype.name = "javascript";
var hero = new Hero();
alert(hero.name);		//output	jscript
delete hero.name;
alert(hero.name);		//output	javascript
扩展内建对象:即给内奸对象添加方法和属性。
//为原型 Array对象增加一个判断的函数
Array.prototype.inArray = function(color){
	for(var i = 0, len = this.length; i < len; i++){
		if(this[i] === color){
			return true;
		}
	}
	return false;
}
//定义一个Array对象
var a = ["red", "green", "blue"];
//测试
alert(a.inArray("red"));		//true
alert(a.inArray("yellow"));		//false

17,继承

l  如果两个类都是同一个实例的类型,那么它们之间存在着某些关系,我们把同一个实例的类型之间的泛化关系称为“继承”。

l  继承关系至少包含三层含义:

l  子类的实例可以共享父类的方法。

l  子类可以覆盖父类的方法或扩展新的方法。

l  子类和父类都是子类实例的“类型”。

l  在javascript中,并不支持“继承”。也就是说,javascript中没有继承的语法。从这个意义上来说,javascript并不是直接的面向对象语言。

(1)原形链继承方法:

是ECMAScript标准制定的默认继承方式。

	function A(){
		this.name = "a";
		this.toString = function(){return this.name};
	}
	function B(){
		this.name = "b";
	}
	function C(){
		this.name = "c";
		this.age = 18;
		this.getAge = function(){return this.age};
	}
	
	B.prototype = new A();		//B继承了A
	C.prototype = new B();		//C继承了B

其实:B.prototype指向的是对象B的原型域,在原型域和对象本体域中都可以封装方法。

l  将对象直接创建在B对象的prototype属性中,并没有去扩展这些对象的原有原型。

l  通过new A ( ) 另创建了一个新的实体,然后用它去覆盖该对象的原型。

l  javascript是一种完全依靠对象的语言,其中没有类(class)的概念。

l  因此,需要直接用new A ( ) 创建一个实体,然后才能通过该实体的属性完成相关的继承工作。

l  完成这样的继承实现之后,对 A ( ) 所进行的任何修改、重写或删除,都不会对 B ( ) 产生影响。

l   

(2)只继承于原型。

function A(){}
A.prototype.name = "a";
A.prototype.toString = function(){return this.name};
	
function B(){}
B.prototype = A.prototype;
B.prototype.name = "b";
	
function C(){}
C.prototype = B.prototype;
C.prototype.name = "c";
C.prototype.age = 18;
C.prototype.getAge = function(){return this.age};

这种方式省去了中间创建的对象,节省了内存空间,但是必须创建空对象才可以完成,创建完空对象之后,需要将这个对象的所有属性和方法都定义在这个对象的原型中才可以。故而也是有局限性的。

(3)浅复制(了解)

//该函数接受一个对象并返回它的副本,核心部分
function extendCopy(p){
	var z = {};	//定义一个空的对象z
	for(var i in p){	//var i =0 ; i < p.length ; i++
		z[i] = p[i];	//都当做数组处理的话,可以理解
	}
	//uber属性:将p作为z的父级,将z指向p的原型
	z.uber = p;
	return z;
}
//定义对象a,但是对象a不是函数对象	
var a = {
	name : "a",
	toStr : function(){return this.name;}
}
//定义对象b,但是对象b不是函数对象	
var b = extendCopy(a);
b.name = "b";
b.toStr = function(){return this.uber.toStr() + " , " + this.name;};
//定义对象c,但是对象c不是函数对象	
var c = extendCopy(b);
c.name = 18;
	
alert(c.toStr());		//output	a , b , 18

通过非函数的方法创建对象,b继承a,c继承b。

非函数的方式是没有原型属性的,只有通过函数方式创建的对象才具有原型属性。

核心部分解析:

这个方法是继承关系的核心方法,通过调用这个方法可以创建两个对象之间的继承关系。

这个方法中,首先定义了一个空的对象z,再将接收的父类对象中的所有属性和方法复制到这个临时的对象z中。uber属性是js中每个对象内置的属性,这里通过z的uber属性指向了p对象,并返回这个临时对象的引用。

注意:虽然uber是指向父类对象p的指针,但是如果不执行复制动作(即循环遍历复制),是无法使用父类的属性和方法的。另外,如果只是执行复制的动作,但是不用uber指向父类对象,虽然父类对象中的属性和方法可以通过子类对象调用,但是这只是将内容复制过来,并不是继承,如果要实现继承,就必须有子类对父类的引用,所以,这二者缺一不可。

浅复制图示:b继承a(参照上述核心方法)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值