您的位置:首页 > Web前端 > JavaScript

Js中容易遗漏的知识点

2014-04-14 14:26 417 查看

Js中容易遗漏的知识点

1.
<script>标签中存在一个defer属性,这个属性是可选的。表示脚本可以延迟到文档完全被解析和显示这后再执行。

2.
<script>标签与<img>标签中相同之处,即他们的Src属性均可以指向当前html页所在域之外的的某个域中的URL.这一点让它功能强大,不过也让它倍受争议。利用jsonp实现js的跨域请求就是通过这个特性来实现的。

3.
浏览器按照<script>元素在页面中出现的顺序对它们依次进行解析。

4.
一般情况下,我们都会把css文件和js文件head中,这就意味着必须等到全部的js代码都被下载、解释、执行完成以后,才能开始呈现页面的内容(浏览器在遇到body标签时才开始呈现内容)。如果有大量的js代码需要被执行,那势必就会造成呈现页面时出现延迟,而延迟期间浏览器将是一片空白。为了避免这个问题,我们可以将js代码放在body体中,放在页面的内容后面。如下例所示:

<html>

<head>

….

</head>

<body>

页面内容

<script></script>

<script></script>

</body>

</html>

5.文档模式,
通过使用文档类型(doctype)来切换实现的。很多初学者并不关注这一概念,但这确实会造成影响。虽然文档模式主要影响CSS内容的呈现,但在某些情况下也会影响到js脚本的解释执行。如果文档开始没有发现文档类型声明,则所有的浏览器都会默认开启混杂模式,但采用混杂模式并不被推荐,因为不同浏览器在这种模式下的行为差异非常大。

浏览器不同、版本不同,其呈现引擎不同,即使相同的文档模式,也可能会产生不同的呈现结果。

6.<noscript>元素
。这个元素基本上很少有人用。包含在<noscript>元素中的内容只会在两种情况下显示出来:

1,浏览器不支持js

2,浏览器支持脚本,但脚本被禁用。

第一种情况现在几乎是不存在的,在特别的情况下才会出现第二种情况。

7.如果在函数中,用Var声明一个变量,则该变量是局部的,即退出函数后,该变量会被销毁。如果没有用Var,则该变量是全局的(不推荐,很难维护)。

8,
ECMAScript不支持任何自定义数据类型的机制。它只包括五种基本数据类弄(Undefined,Null,Boolean,Number和String.还有一种复杂数据类型-Object,Object本质上是一组无序的名值对组成的)。利用typeof 操作符可以查看给定变量的数据类型

9.
在其它编程语言中,任何数值除以0都会产生一个错误,从而停止执行后面的代码,但在ECMAScript中,任何数值除以0都会返回NaN,并不会影响其它代码的执行。

10.
各数据类型之间的转换非常重要。



11.
可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean类型的值。转换规则如下:



12.

浮点数值计算会产生传入误差的问题,如0.1+0.2并不等于0.3,这是使用基于IEEE754数值的浮点计算的通病,ECMAScript并非独此一家,其它使用相同数据格式的语言也存在这个问题。因此,永远不要测试某个特定浮点数值。

13.with语句
With语句的作用是将代码的作用域设置到一个特定的对象中,语法如下:

With(expression) statement

定义with语句的目的主要是为了简化多次编写同一个对象的工作,如:

var qs=location.search.substring(1);

var hostname=location.hostname;

var url=location.href;

with(location)

{

var qs2=search.substring(1);

var hostname2=hostname;

var url2=href;

alert(qs2);

alert(hostname2);

alert(url2);

}

14.函数参数
ECMAScript函数不介意传递进来多少个参数,也不介意传递进来的参数是什么类型。即使你定义函数时只接收两个参数,在调用这个函数时也不需要一定要传递两个参数,你可以传递一个,也可以传递三个,解析器永远不会报错。原因是ECMAScript函数的参数在内部是用一个数组表示的,函数接收到的永远是这个数组,而不关心数组中包含哪些参数。实际上,在函数体内可以使用arguments对象来访问这个参数对象,从而获得传递的参数。

Arguments对象只和数组非常相似,但并不是数组(Array)的实例。

如下:

function sayHello()

{

var name=arguments[0]!=null?arguments[0]:"kang";

console.info("Hello "+name+",Welcome to China ");

}

sayHello('huilai');

sayHello();

也正因为这个特性,函数没有签名,真正的函数重载是不可能做到的。

function add(num)

{

console.info(num+100);

}

function add(num1,num2)

{

console.info(num1+200);

}

add(100);

add(100,2);

结果会输出2个300.由此可以看出,函数名相同,后定义的函数将前面定义的函数覆盖了。

15.基本类型与引用类型
ECMAScript变量包含两种不同数据类型的值:基本类型值和引用类型值。所谓的基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。而引用类型值则是指那些保存在堆内存中的对象,意思是变量中保存的实际上是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。

在将一个值赋给变量时,解释器必须确定这个值是基本类型还是引用类型。5种基本类型:Undefined,Null,Number,Boolean,String,这五种类型的值在内存中分别占有固定大小的空间,因此可以把它们保存在栈内存中,而且,这样可以提高变量的查询速度。对于保存基本类型值的变量,我们说它是按值访问的,因为它们直接操作的是它们实际保存的值。

如果赋给变量的值是引用类型,则必须在堆内存中为这个值分配内存,因为这种值的大小不确定。但内存地址的大小是固定的,因此可以将内存地址保存在栈内存中。这样,当查询引用变量时,就可以先在栈中读取内存地址中,然后再到堆内存中找到相应的值。这种访问方式我们称为按引用访问,因为变量操作的是值的引用,而不是实际值。

当从一个变量向另一个变量复制值时,会在栈中创建一个新值,然后把该值复制到为新变量分配的位置上。如下:

Var num1=5;

Var num2=num1;

用num1来初始化num2后,这样num2中保存的也是5,但num1中的5与num2中的5是完全独立的,num2中的值只是num1中值的一个副本。此后这两个变量可以参与任何操作而互不影响。

当复制引用变量时,复制的引用的副本,也就是内存地址的副本。所以,这两个变量指向的仍然是同一个对象。操作任何一个都会影响另一个。

var person={

name:"kang",

age: '24',

sex: "m"

}

var p=person;

p.age=25;

console.info(person.age); //输出的值为2

ECMAScript中函数的参数传递都是按值传递的(java也是)。也就是说,函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。

16
前面提到我们可以用typeof操作符来检测变量值是什么类型。但是,在检测引用类型值的时候,这个操作符的用处不大。通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此,我们可以使用instanceof操作符,语法如下:

Var result= variable instanceof constructor

如果变量是给定引用类型(由构造函数表示)的实例,那么instanceof操作符会返回true.

所有的引用类型的值都Object的实例,所以在检测一个引用类型值和Object构造函数时,instanceof操作符会始终返回True.

17
.数组的length属性很有特点,它不是只读的。因此,通过设置这个属性,可以从数组的未尾移除项或向数组中添加新项。如下:

var array=['red','blue','yellow'];

array.length=2;

console.info(array[2]); //undefined

array.length=4;

array[3]='black';

console.info(array[3]); //black

数组提供了类似于其它数据结构行为的方法,如push()和pop()方法可以使得数组的行为像栈,而shift()和push()方法可以使得数组的行为像队列。

18.Function类型。ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype.其中length表示函数希望接收的命令参数的个数。而对于prototype属性,则是ECMAScript中最耐人寻味的了。对于引用类型而言,prototype是保存他们实例方法的真正所在。在创建自定义引用以及实现继承时,prototype属性是极为重要的。

每个函数都包含两个非继承来的方法:apply()和call().这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象的值。首先,apply()方法授受两个参数,一个是其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array数组,也可以是arguments对象。如下:

function sum(num1,num2)

{

return num1+num2;

}

function applySum1(num1,num2)

{

//this表明函数运行在applySum2()函数所在的作用域内

return sum.apply(this,arguments);

}

function applySum2(num1,num2)

{

return sum.apply(this,[num1,num2]);

}

console.info(applySum1(10,10));

console.info(applySum2(10,20));

call()与apply()的作用一样,它们的唯一区别就是call()函数传递参数时必须直接传递。如下:

apply()和call()最强大的用武之地是能够扩充函数赖以运行的作用域。

window.color="red";

var o={color:"blue"};

function sayColor()

{

console.info(this.color);

}

sayColor();

sayColor.call(this);

sayColor.call(window);

sayColor.call(o);

18.Global对象
所有在全局作用域内定义的属性或函数,都是Global对象的属性。像isNaN(),parseInt(),parseFloat()等方法都是Gobal对象的方法。

EncodeURL()和encodeURLComponet()方法是另外两个很重要的方法。

对应的,有decodeURL()和decodeURLComponent()方法。

Eval()方法是ECMAScript语言中最强大的一个方法,它就像一个完整的ECMAScript解析器,它只接受一个方法,即要执行的ECMAScript字符串。

但这个函数非常危险,使用时应该特别谨慎,特别是在用户输入数据的时候,否则,会产生代码注入。

19.面向对象编程
我们可以通过以下方法来创建一个Object实例,并且为它添加属性和方法。

var person={

name:'kang',

age:24,

sex:'m',

sayName:function()

{

console.info(this.name);

}

}

但它存在一个很大的问题,即每次创建一个对象时都需要写大量重复性代码,于是出现了工厂模式来创建对象。

function createPerson(name,age,sex)

{

var person=new Object();

person.name=name;

person.age=age;

person.sex=sex;

person.showName=function()

{

console.info(person.name);

}

return person;

}

var person2=createPerson('huilai',22,'f');

person2.showName();

虽然解决了代码重复问题,但还存在一个问题,即无法知道一个对象的类型。于是,构造函数模式出现了

function Person(name,age,sex)

{

this.name=name;

this.age=age;

this.sex=sex;

this.showName=function()

{

console.info(this.name);

}

}

var person3=new Person('lai',24,'f');

person3.showName();

console.info(person3 instanceof Person);

任何函数,只要通过new操作符来调用,那它就可以作为构造函数。而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样





构造函数模式虽然好用,但也存在问题。它的问题是:每个方法都要在每个实例上重新创建一遍

var person3=new Person('lai',24,'f');

var person4=new Person('hui',23,'m');

console.info(person3.showName==person4.showName); //false

在上面这段代码中,person3和person4都有一个showName()方法,但这两个方法不是同一个Function的实例。Why?ECMAScript中的函数是对象,因此每定义一个函数,也就是实例化了一个对象。从逻辑角度讲,此时的构造函数也可以这样定义:

function Person(name,age,sex)

{

this.name=name;

this.age=age;

this.sex=sex;

this.showName= new function("console.info(this.name)")

}

从这个角度看,更容易明白每个Person实例都包含一个不同的Function实例的本质。

创建两个完成同样任务的Function实例实在没有必要,况且有this对象在,根本没有必要在执行代码前就把函数绑定到特定对象上面。

为了解决上述问题,于是原型模式出现了。

原型模式(重中之重)
我们创建的每个函数都有一个prototype属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。换句话说,不必在构造函数中定义对象信息,可以直接把这些信息添加到原型对象中。

如下:

function Student(age,sex)

{

this.age=age;

this.sex=sex;

}

Student.prototype={

name:'kang',

showName:function()

{

console.info(this.name);

}

}

var s1=new Student(23,'f');

var s2=new Student(24,'m');

console.info(s1.name);

console.info(s2.name);

console.info(s1.showName==s2.showName); //true



关于对prototype的理解,可以见《JAVAScript高级编程》Page 120.

同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。如下:

function hasPrototypeProperty(object,name)

{

return !object.hasOwnProperty(name)&&(name in object);

}

除此之外,还有寄生构造函数模式和稳妥构造函数模式。可以自行百度。

20.继承
许多oo语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于在ECMAScript中函数没有签名,所以无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

思想:让一个引用类型继承另一个引用类型的属性和方法。我们让原型对象等于另一个类型的实例,此时的原形对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。

如下:

function SuperType()

{

this.property="this is super";

}

//这样是相当于重写prototype属性,所以要显示说明其构造函数是什么类型。

SuperType.prototype={

//在这可以直接一步到位

constructor:SubType,

getSuperValue:function()

{

return this.property;

}

}

function SubType()

{

this.property="this is sub";

}

//让原型对象等于类型的实例。也就继承了SuperType.本质是重写原型对象,代之以一个新类型的实例。因为这一步的存在,不能使用对象字面量

SubType.prototype=new SuperType();

//给原型添加新方法一定要放在替换原型的语句之后。

SubType.prototype.getSubValue=function()

{

return this.property;

}

//显式说明构造函数类型。

//SubType.prototype.constructor=SubType;

var instance =new SubType();

console.info(instance.getSuperValue()); //"this is sub"

console.info(instance.getSubValue());

//由于原型链的关系,我们可以说instance是Object,SubType,SuperType中任一类型的实例。

console.info(instance instanceof Object);

console.info(instance instanceof SuperType);

console.info(instance instanceof SubType);

console.info(instance.constructor);

//调用的实际上是object.prototype中的toString()

console.info(instance.toString());

//只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。

console.info(Object.prototype.isPrototypeOf(instance));

console.info(SuperType.prototype.isPrototypeOf(instance));

console.info(SubType.prototype.isPrototypeOf(instance));关系图如下:



我们知道,所有的引用类型都继承了Object,这个继承也是通过原型链来实现的。

原型链虽然强大,但也存在问题,如不能向父类传递参数。可以通过"借用构造函数"的技术(主要是通过apply()或call()方法来实现)来克服这个问题。一般情况下,都是原型链和借用构造函数一起使用,很少会单独使用。

function SuperType(name)

{

this.colors=['red','blue','yellow'];

this.name=name;

}

SuperType.prototype={

constructor:SubType,

showName:function()

{

return this.name;

}

}

function SubType(name,age)

{

SuperType.apply(this,[name]);

this.age=age;

}

SubType.prototype=new SuperType();

SubType.prototype.showAge=function()

{

return this.age;

}

var sub=new SubType('kang',24);

//下面返回的是True,也就是说name和colors成为为SubType实例的属性。

console.info(sub.hasOwnProperty("name")); //true

console.info(sub.hasOwnProperty("colors")); //true

console.info(sub.showName());

var sub2=new SubType('huilai',23);

console.info(sub2.showName());

这样,两个不同的SubType实例就即可以有自己的属性,也可以共用相同的方法了。

组合继承融合了原型链和借用构造函数的优点,成为JavaScript中最常用的继承模式。而且,instanceof和isPrototypeOf也能够识别基于组合继承创建的对象。

不过,大家注意到没有,在组合继承模式中,一共调用了两次SuperType的构造函数。在就导致了在SubType实例中有name和colors属性,在SubType的原型中也有name和colors属性。接下来,我们会想办法解决这个问题。

function inheritPrototype(SubType,SuperType)

{

function temp(){}; //创建临时对象

temp.prototype=SuperType.prototype;

var t=new temp(); //创建临时实例

t.constructor=SubType;

SubType.prototype=t; //还是将实例赋给原型

}

function SuperType(name)

{

this.colors=['red','blue','yellow'];

this.name=name;

}

SuperType.prototype={

showName:function()

{

return this.name;

}

}

function SubType(name,age)

{

SuperType.apply(this,[name]);

this.age=age;

}

inheritPrototype(SubType,SuperType);

//增强原型,添加方法

SubType.prototype.showAge=function()

{

return this.age;

}

var sub=new SubType('kang',24);

//下面返回的是True,也就是说name和colors成为了SubType实例的属性。

console.info(sub.hasOwnProperty("name")); //true

console.info(sub.hasOwnProperty("colors")); //true

console.info(sub.showName());

var sub2=new SubType('huilai',23);

console.info(sub2.showName());

上面这种方法叫寄生组合继承模式。它的高效率体现在调用了一次SuperType的构造函数,因此避免了在SupType.prototype上创建不必要的属性。

寄生组合继承模式被认为是引用类型最理想的继承范式。

21.匿名函数
匿名函数即没有名称的函数,有时也称为拉姆达函数(lambda).

Arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: