script闭合标签
在使用<script>嵌入 JavaScript 代码时,当浏览器遇到字符串"</script>"时,就会认为那是结束的</script>标签。而通过转义字符“/”可以解决这个问题,例如:
<script type="text/javascript">
function sayScript(){
alert("<\/script>");
}
</script>
标识符
所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是字母、下划线(_)、美元符号($)或数字组合起来的一或多个字符,其中数字不能放首位。
null 和 undefined
- null == undefined
- typeof null === "object"
- 无论在什么情况下都没有必要把一个变量的值显式地设置为 undefined
- 只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存 null 值。
这样做不仅可以体现 null 作为空对象指针的惯例,而且也有助于进一步区分 null 和 undefined。
字符串
- 对非字符串执行字符串操作,目标为其
toString()
方法返回值
数字
- 对对象执行的数字操作,目标为其
valueOf()
方法返回值
浮点数
- 浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。例如, 0.1+0.2和0.1*3的结果不是 0.3,而是 0.30000000000000004。
因此,永远不要测试某个特定的浮点数值。
关于浮点数值计算会产生舍入误差的问题:这是使用基于IEEE754 数值的浮点计算的通病, ECMAScript 并非独此一家;其他使用相同数值格式的语言也存在这个问题
大小限制
- 由于内存的限制, ECMAScript 并不能保存世界上所有的数值。 ECMAScript 能够表示的最小数值保存在 Number.MIN_VALUE 中——在大多数浏览器中,这个值是 5e-324;能够表示的最大数值保存在Number.MAX_VALUE 中——在大多数浏览器中,这个值是1.7976931348623157e+308。
如果某次计算的结果得到了一个超出 JavaScript 数值范围的值,那么这个数值将被自动转换成特殊的 Infinity 值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转换成 Infinity(正无穷)。
Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(换句话说,是不是位于最小和最大的数值之间),可以使用 isFinite()函数。这个函数在参数位于最小与最大数值之间时会返回 true,如下面的例子所示:
var result = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result)); //false
尽管在计算中很少出现某些值超出表示范围的情况,但在执行极小或极大数值的计算时,检测监控这些值是可能的,也是必需的。 - 访问 Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也可以得到负和正 Infinity 的值。可以想见,这两个属性中分别保存着-Infinity 和Infinity。
NaN
- 任何数字除以0变为NaN,无法转换为数字的强制转化时也会变成NaN
- NaN==NaN为false,因此需要判断目标是否为NaN时采用isNaN(NaN)
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false( 10 是一个数值)
alert(isNaN("10")); //false(可以被转换成数值 10)
alert(isNaN("blue")); //true(不能转换成数值)
alert(isNaN(true)); //false(可以被转换成数值 1)
Object
ECMA本地对象(宿主对象不一定)中Object 的每个实例都具有下列属性和方法:
- constructor:保存着用于创建当前对象的函数。
- hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如: o.hasOwnProperty("name"))。
- isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型。
- propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句
来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。 - toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
- toString():返回对象的字符串表示。
- valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值
相同。
布尔操作符
- 逻辑非 ! 逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再
对其求反。- 因此!!等价于Boolean()
- 逻辑与 &&
- 依次判断直到为false,并返回最后一个值
- 逻辑或 ||
- 依次判断直到为true,并返回最后一个值
对象的类型 基本类型值和引用类型值
- 基本类型值:五大基本类型
- 引用类型值:对象 数组 RegExp Date Function
- 无法给基本类型值添加自定义属性,如var str='some text';
- 但如var str = new String("some text");则可添加自定义属性
- 事实上每当如1中声明一个基本类型值的时候,后台就会如2中创建一个对应的基本包装类型的对象
var str1 = "Nicholas";
str1.age = 27;
alert(str1.age); //undefined
var str2 = new String("Nicholas");
str2.age = 27;
alert(str2.age); //27
- 复制引用类型时,复制的其实是指针而非对象本身,因此两变量指向同一对象
- 注意函数参数的传递方式
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
- 确定一个值是哪种基本类型可以使用 typeof 操作符,而确定一个值是哪种引用类型可以使用instanceof 操作符。
数组的迭代和归并
迭代
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
- filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
- forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。
归并
- reduce()
- reduceRight():类似reduce,但顺序相反
以上方法都不会修改数组中的包含的值。
函数
- 函数是对象,函数名是指针
- 对指针的改动(如添加属性)会反应到对象上,但是对指针的重新赋值会解除绑定,不会对对象产生影响
- caller和callee
基本包装类型
- 创建实例时均优先用直接方式创建,因采用对象方式新建会有些反常理的因素
Boolean
Number
String
eval方法
- 严格模式下无法使用
- eval()会将字符串参数插入到所在位置并当做代码执行
- 该功能过于强大,因此尤其在涉及用户输入内容时,应该谨慎使用,防止被代码注入
var a='alert ("hello world")';
eval(a);
对象
两种属性
在ES5中,我们为了描述属性(property)的各种特征,定义了特性(attribute)。在JavaScript中不能直接访问特性,我们把它放在两对方括号中,例如[[Enumerable]]。
-
数据属性
- 数据属性一般用于存储数据数值
- [[Configurable]]:默认为true。表示能否通过delete删除属性从而重新定义属性,能否修改属性特性(除了writable),或者能否把属性修改为访问器属性;
- [[Enumerable]]:默认为true。表示能否通过for-in循环返回属性;
- [[Writable]]:默认为true。表示能否修改属性的值。
- [[Value]]:默认值为undefined。表示包含属性的数据值。读写属性值都从这个位置进行。
- 数据属性一般用于存储数据数值
var person = {
name: "Scott"
}
Object.defineProperty(person,"name",{
writable:false;
})
console.log(person.name); //"Scott"
person.name = "Evan";
console.log(person.name); //"Scott"
- 访问器属性
-
访问器属性不包含数据值。它包含一对getter和setter函数。当读取访问器属性时,会调用getter函数并返回有效值;当写入访问器属性时,会调用setter函数并传入新值,setter函数负责处理数据。该属性有四个特性:
- [[Configurable]]:默认为true。表示能否通过delete删除属性从而重新定义属性,能否修改属性特性,或者能否把属性修改为访问器属性;
- [[Enumerable]]:默认为true。表示能否通过for-in循环返回属性;
- [[Get]]:读取属性时调用的函数,默认为undefined;
- [[Set]]:写入属性时调用的函数,默认为undefined。
访问器属性不能直接定义,必须通过Object.defineProperty()函数定义,例如:
-
var person = {
_name: "Scott",
_age: 24,
_tel: 86247
};
//name属性为只读的
Object.defineProperty(person,"name",{
get: function(){
return this._name;
}
});
//age属性为只写不可读的
Object.defineProperty(person,"age",{
set: function(p){
this._age = p;
}
});
//tel属性为可读可写的
Object.defineProperty(person,"tel",{
get:function(){
return this._tel;
},
set: function(p){
this._tel = p;
}
});
console.log(person.name); //"Scott"
person.name = "Evan";
console.log(person.name); //"Scott",对name属性的修改无效
console.log(person.age); //undefined,不可读属性
person.age = 25;
console.log(person._age); //25,已经修改
console.log(person.tel); //"86247",可读属性
person.tel = "13975";
console.log(person.tel); //"13975",可以修改
属性前面的下划线表示只能通过对象方法访问的属性。当我们使用person.name时实际上调用的是name属性的getter函数,为person.name赋值时调用的是name属性的setter函数,这样属性和访问器之间的关系就很清晰了。
读取属性的特性
- Object.getOwnPropertyDescriptor(),两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable、 enumerable、 get 和 set;如果是数据属性,这个对象的属性有 configurable、 enumerable、 writable 和 value。
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value); //2004
alert(descriptor.configurable); //false
工厂模式
- 可以无数次地调用函数构建对象
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
寄生构造函数模式
- 除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
构造函数模式
- 使用new之后this指向新对象而非window,且自带隐式返回语句 : return this
- alert(person1 instanceof Object); //true
- alert(person1 instanceof Person); //true
- alert(person1.constructor == Person); //true
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
- 几种调用构造函数方法
// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到 window
window.sayName(); //"Greg"
// 在另一个对象的作用域中调用 **(伪造对象经典继承)**
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
原型链
- 几种基本数据类型的对象方法,其实处于构造函数的prototype中
判断原型链属性还是自身属性
- hasOwnProperty()方法,只在给定属性存在于对象实例中时,才会返回 true。
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //"Greg"—— 来自实例
alert(person1.hasOwnProperty("name")); //true
delete person1.name;
alert(person1.name); //"Nicholas"—— 来自原型
alert(person1.hasOwnProperty("name")); //false
判断属性是否存在于自身或原型链
- in方法 只要存在该属性即为true
delete person1.name;
alert(person1.name); //"Nicholas" —— 来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
更方便的原型封装方式:令原型=对象
- 前面例子中每添加一个属性和方法就要敲一遍 Person.prototype。为减少不必要的输入,也为了从视觉上更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下面的例子所示。
function Person(){}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
- 代价是 constructor 属性不再指向 Person 了。
- 每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性(即constructor 属性位于原型上而非实例上)。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。
- 此时可以像下面这样特意将它设置回适当的值。
function Person(){}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
原型的动态性
- 实例与原型间为松散连接,只是指针而非副本;
- 也因此,修改实例从原型继承的属性会导致,原型中对应属性也发生变化
- 因此只要在调用时,原型中有该属性即可,而非实例创建时需要该属性;若原型被重新定义,则与原指针断开连接(类似module.exports和exports)
function Person(){
}
var friend = new Person();
Person.prototype.hello='nihao';
Person.prototype = {
constructor: Person,
name : "Nicholas",
sayName : function () {
alert(this.name);
}
};
console.log(friend.hello); //nihao
var friend1 = new Person();
friend1.sayName(); //error
console.log(friend1.hello); //undefined
原型链的问题解决
借用构造函数
- 缺点:方法都在构造函数中定义,未用到原型链,函数复用无从谈起
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了 SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
组合继承
- 组合继承,有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//继承属性
SuperType.call(this, name);//第二次调用,用自身属性覆盖原型链的属性,不怕联动
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();//第一次调用,为获取方法,却也获取了属性
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
原型式继承
寄生式继承
- 即如下的object()
组合寄生式继承
- 弥补组合继承需要调用两次的缺陷
- 把超类的原型作为形参(不再联动)给予新对象,对象再给予子类的原型,最终完成原型===>原型的步骤,却只调用超类一次