<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>JavaScript Study 2015.11.9--</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
</style>
<script type="text/javascript">
//JavaScript = ECMAScript(function,closure,OO) + DOM + BOM
/*
var sColor = "red";
//alert(sColor.length); //输出 "3"
var bFound = false;
//alert(bFound.toString()); //输出 "false"
var iNum1 = parseInt("12345red"); //返回 12345
var iNum1 = parseInt("red12345"); //返回 NaN
//alert(iNum1);
//alert(inum1); // error: inum1 is not defined
var iNum1 = parseInt("0xA"); //返回 10
var iNum1 = parseInt("56.9"); //返回 56
var iNum1 = parseInt("red"); //返回 NaN
//解析16进制的值, 第二个参数
var iNum1 = parseInt("AF", 16); //返回 175
//alert(iNum1);
//2进制
var iNum1 = parseInt("10", 2); //返回 2
//8进制
var iNum2 = parseInt("10", 8); //返回 8
//10进制
var iNum3 = parseInt("10", 10); //返回 10
//如果10进制数,包含0作前导,最好使用第二个参数为10
var iNum1 = parseInt("010"); //返回 8
var iNum2 = parseInt("010", 8); //返回 8
var iNum3 = parseInt("010", 10); //返回 10
var b1 = Boolean(""); //false - 空字符串
var b2 = Boolean("hello"); //true - 非空字符串
var b1 = Boolean(50); //true - 非零数字
var b1 = Boolean(null); //false - null
var b1 = Boolean(0); //false - 零
var b1 = Boolean(new Object()); //true - 对象
ECMAScript 中可用的 3 种强制类型转换如下:
Boolean(value) - 把给定的值转换成 Boolean 型;
Number(value) - 把给定的值转换成数字(可以是整数或浮点数);
String(value) - 把给定的值转换成字符串;
var b1 = Boolean(""); //false - 空字符串
var b2 = Boolean("hello"); //true - 非空字符串
var b3 = Boolean(50); //true - 非零数字
var b4 = Boolean(null); //false - null
var b5 = Boolean(0); //false - 零
var b6 = Boolean(new Object()); //true - 对象
document.write(b1+"<br>");
document.write(b2+"<br>");
document.write(b3+"<br>");
document.write(b4+"<br>");
document.write(b5+"<br>");
document.write(b6+"<br>");
//特别注意
document.write(Number(false)+"<br>");// 0
document.write(Number(true)+"<br>");// 1
document.write(Number(undefined)+"<br>");// NaN
document.write(Number(null) +"<br>");// 0
document.write(Number("1.2")+"<br>");// 1.2
document.write(Number("12")+"<br>");// 12
document.write(Number("1.2.3")+"<br>");// NaN
document.write(Number(new Object())+"<br>");// NaN
document.write(Number(50) +"<br>");// 50
var s1 = String(null); //"null"
var oNull = null;
var s2 = oNull.toString(); //会引发错误
//两种方式都可以
var obj = new Object;
var obj1 = new Object();
Object 对象具有下列属性:
constructor
对创建对象的函数的引用(指针)。对于 Object 对象,该指针指向原始的 Object() 函数。
Prototype
对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例。
Object 对象还具有几个方法:
hasOwnProperty(property)
判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty("name"))
IsPrototypeOf(object)
判断该对象是否为另一个对象的原型。
PropertyIsEnumerable
判断给定的属性是否可以用 for...in 语句进行枚举。
ToString()
返回对象的原始字符串表示。对于 Object 对象,ECMA-262 没有定义这个值,所以不同的 ECMAScript 实现具有不同的值。
ValueOf()
返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。
*/
/*
//函数
function sayHi() {
if (arguments[0] == "bye") {
return;
}
alert(arguments[0]);
}
//下面的调用方式都不报错
sayHi();
sayHi("hehe");
function howManyArgs() {
alert(arguments.length);
}
howManyArgs("string", 45);
howManyArgs();
howManyArgs(12);
//模拟函数重载
function doAdd() {
if(arguments.length == 1) {
alert(arguments[0] + 5);
} else if(arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); //输出 "15"
doAdd(40, 20); //输出 "60"
function doAdd(){
var j =0;
for(var i=0;i<arguments.length;i++){
j= j+ arguments[i];
}
alert(j);
}
doAdd(20);
doAdd(20,40);
//函数的覆盖
function doAdd(iNum) {
alert(iNum + 20);
}
function doAdd(iNum) {
alert(iNum + 10);
}
doAdd(10); //输出 "20"
var doAdd = new Function("iNum", "alert(iNum + 20)");
var doAdd = new Function("iNum", "alert(iNum + 10)");
doAdd(10);
//画出内存结构
function fn(){
return 100;
}
//x 指向 fn 所指向的内存区域
var x = fn;
var y = fn();
var z = x();
//定义对象
function Person(name,age,address){
this.name = name;
this.age = age;
this.address = address;
this.say = function(){
alert(this.name+","+this.age+","+this.address);
}
}
var p1 = new Person("p1",23,"bj");
var p2 = new Person("p2",24,"nj");
//存在的问题:
//每创建一个对象p,都会在内存中有一份Person的say function拷贝,会导致内存空间的浪费。
//而实际的对象的创建,应该是:每创建一个对象p,只会创建一个变量,指向内存中的say function的同一区域。
//p1.say();
// p2.say();
//定义对象的方式1:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
alert(this.name+","+this.age);
}
var p1 = new Person("p1",20);
p1.say();
var p2 = new Person("p2",21);
p2.say();
//定义对象的方式2:
person = new Object();
person.name = "li";
person.age = 34;
person.say= function(){alert(person.name);}
person.say();
//定义对象的方式3:
person={
name:"li",
age:34,
say:function(){
alert(this.name);
}
}
person.say();
var person ={
name:"li",
age:34,
say:function(){
alert(this.name);
}
}
for(x in person){
alert(x); // name,age,say
}
*/
/*
//http://www.jb51.net/article/24101.htm
//闭包
闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
//作用:
一个是前面提到的可以读取函数内部的变量,
另一个就是让这些变量的值始终保持在内存中。
//什么时候使用:
1、保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
2、在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
3、通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的
function Constructor(...) {
var that = this;
var membername = value;
function membername(...) {...}
}
以上3点是闭包最基本的应用场景,很多经典案例都源于此。
//这是一个最简单的闭包
var sMessage = "hello closure";
function sayHelloClosure(){
alert(sMessage);
}
sayHelloClosure();
//在上面这段代码中,脚本被载入内存后,并没有为函数 sayHelloWorld() 计算变量 sMessage 的值。
//该函数捕获 sMessage 的值只是为了以后的使用,也就是说,解释程序知道在调用该函数时要检查 sMessage 的值。
//sMessage 将在函数调用 sayHelloWorld() 时(最后一行)被赋值,显示消息 "hello world"。
var iBaseNum = 10;
function addNum(inum1,inum2){
//定义一个闭包
function doAdd(){
return inum1+iNum2+iBaseNum;
}
//调用
return doAdd();
}
//这里,函数 addNum() 包括函数 doAdd() (闭包)。
//内部函数是一个闭包,因为它将获取外部函数的参数 iNum1 和 iNum2 以及全局变量 iBaseNum 的值。
//addNum() 的最后一步调用了 doAdd(),把两个参数和全局变量相加,并返回它们的和。
//这里要掌握的重要概念是,doAdd() 函数根本不接受参数,它使用的值是从执行环境中获取的。
//可以看到,闭包是 ECMAScript 中非常强大多用的一部分,可用于执行复杂的计算。
//提示:就像使用任何高级函数一样,使用闭包要小心,因为它们可能会变得非常复杂。
//这里很关键,也很难理解,需要画内存图。
function foo(x){
var tmp = 3;
return function(y){
alert(x + y + (++tmp) );
}
}
//这里才是闭包
var bar = foo(2); // 相当于 var bar = function(y){alert(2+y+(++tmp));} 但还是有所不同。
bar(10);//16
bar(10);//17 由于tmp仍存在于bar闭包的内部,所以它还是会自加1,而且你每次调用bar时它都会自加1.
//闭包的理解:
//http://kb.cnblogs.com/page/105708/
//Javascript的垃圾回收机制
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。
如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
var func = object.getNameFunc(); //返回匿名的闭包函数
var returnName = func();
//alert(returnName);
//alert(object.getNameFunc()()); //The Window 为什么不是: My Object
function outerFun()
{
var a=0;
function innerFun()
{
a++; //a 的值不会被GC回收。
alert(a);
}
return innerFun; //注意这里
}
var obj = outerFun();
obj(); //结果为1
obj(); //结果为2
obj(); //3 ....
var obj2 = outerFun();
obj2(); //结果为1
obj2(); //结果为2
function outerFun()
{
var a =0;
alert(a);
}
var a=4;
outerFun();//0
alert(a);//40
function outerFun()
{
//没有var
a =0;
alert(a);
}
var a=4;
outerFun();//0
alert(a);//0
//作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4; 并改变其值.
*/
//作用域链
// http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
//OO面向对象
/*
function Cat(name,age){
this.name = name;
this.age = age;
this.type = "动物";
this.say = function(){
alert(this.name);
}
}
var cat1 = new Cat("haha",3);
var cat2 = new Cat("wwww",4);
上面的定义方式,存在的问题:
cat1, cat2 ...的
type, say 都是相同的内容,在new的时候,会给每个cat1,cat2,都创建一块内存,用来存储type,say.
这就造成了内存空间的浪费。
可以使用下面的改良的方式定义:
function Cat(name,age){
this.name = name;
this.age = age;
}
Cat.prototype.type = "动物";
Cat.prototype.say = function(){
alert(this.name);
}
*/
/*
上面的定义方式,存在的问题:
虽然解决了内存空间浪费的问题,但在形式上看起来,并不像java里的Class的定义方式。
可以使用下面的改良的方式定义:
function Cat(name,age){
this.name = name;
this.age = age;
if(!this.type)
Cat.prototype.type = "动物";
if(!this.say){
Cat.prototype.say = function(){
alert(this.name);
}
}
}
var cat1 = new Cat("haha",3);
var cat2 = new Cat("wwww",4);
cat1.say();
cat2.say();
*/
//继承
//http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
/*
// 一、 构造函数绑定
//第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:
//父类
function Animal(){
this.species = "动物";
}
//子类
function Cat(name,color){
Animal.apply(this, arguments);//
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
// 二、 prototype模式
//第二种方法更常见,使用prototype属性。
//如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
//父类
function Animal(){
this.species = "动物";
}
//子类
function Cat(name,color){
this.name = name;
this.color = color;
}
//实现继承
Cat.prototype = new Animal();//重写了Cat.prototype
Cat.prototype.constructor = Cat;//将新的Cat.prototype.constructor再指向Cat
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
// 三、 直接继承prototype (有问题)
//第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
//现在,我们先将Animal对象改写:
function Animal(){}
Animal.prototype.species = "动物";
//然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;//这一句实际上把Animal.prototype对象的constructor属性也改掉了!
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
//与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
//所以,上面这一段代码其实是有问题的。
//四、 利用空对象作为中介
//由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。
/*
function Animal(){}
Animal.prototype.species = "动物";
function Cat(name,color){
this.name = name;
this.color = color;
}
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
//F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。
//
function Animal(){}
Animal.prototype.species = "动物";
function Cat(name,color){
this.name = name;
this.color = color;
}
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
extend(Cat,Animal);//YUI提供的实现继承的方式
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
//五、 拷贝继承
//上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。
function Animal(){}
Animal.prototype.species = "动物";
function Cat(name,color){
this.name = name;
this.color = color;
}
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
extend2(Cat, Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
*/
/*
//一、什么是"非构造函数"的继承?
//比如,现在有一个对象,叫做"中国人"。
var Chinese = {
nation:"中国"
}
var Doctor ={
career:"医生"
}
//1, object方法
function object(o) {
function F() {} //空对象
F.prototype = o; //父对象
return new F();
}
//这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。
//2, 浅拷贝方法
//除了使用"prototype链"以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。
function extendCopy(p){
var c = {};
for(var i in p){
c[i] = p[i];
}
return c;
}
//var Doctor = extendCopy(Chinese);
//Doctor.career = '医生';
//alert(Doctor.nation); // 中国
//但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
Chinese.birthPlaces = ['北京','上海','香港'];
var Doctor = extendCopy(Chinese);
Doctor.birthPlaces.push('厦门');
alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门
//3, 深拷贝方法 (JQuery使用的实现继承的方式)
function deepCopy(p,c){
var c = c || {};
for(var i in p){
if(typeof p[i] === 'object'){
c[i] = (p[i].constructor === Array)? []:{};//? 什么意思
deepCopy(p[i],c[i]);//递归
}else{
c[i] = p[i];
}
}
return c;
}
Chinese.birthPlaces = ['北京','上海','香港'];
var Doctor = deepCopy(Chinese);
Doctor.birthPlaces.push('厦门');
alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门
*/
</script>
</head>
<body>
<div id="wrap">
</div>
</body>
</html>