<!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>
时间: 2024-10-14 05:17:43