Txt
1、理解对像
var person = {name:'nic'}
1.1 属性类型
1.1.1 数据属性
Configurable delete true
Enumerable for in true 能否通过for-in循环返回属性 true能 | false不能
Writeable 修改 true
Value 写入 undefined
Object.defineProperty( person, 'name' , { Writable:false,value:'pill' } )
1.1.2访问器属性
读访问:getter 写访问:setter
configurable delete true
Enumerable for in true
Get undefined 只定义,证明只能读 不能写
Set undefined 只定义,证明只能写 不能读
访问器属性不能直接定义,须使用Object.defineproperty()
var person = { _name:'pill' } //定义对像访问属性
Object.defineProperty(person,'name',{ get:function(){ return this._name }; }) //设置访问器
person.name //访问,即相当于访问name的get函数,直接person._name也可得到相同结果,这样做访问器就无意义了
//即设置 person.name访问器来访问 person._name,相当于加减权限
1.1.3 定义多个属性
Object.defineProperties( person,{ _year:{value:2004}, )
var book = {} Object.defineProperties( book,{ year:{value:2004},edition:{value:2},Year{ get:function(){return this.year},set:function(nv){if(nv > 2004){this.year = nv; this.edition += nv -2004;}} } } ) //year | edition 数据属性,Year :访问器属性
1.1.4 读取属性特性
Object.getownPropertyDescriptor( 属性所在对像, 读取其描述符的属性名称 ) 给定属性描述
2 创建对像
2.1工厂模式
function createPerson( name,age,job){
var o = new Object;
o.name = name;
return o;
}
var person1 = createPerson( 'nic' )
无法解决;如何识别一个对像的类型;
2.2构造函数模式 ---创建对像的函数
function Person ( name,age,job ){
this.name = name;
this.age = age;
this.sayName = function() {
alert(this.name);
}
}
var p1 = new Person( 'nic','29','software Engineer' ) // p1保存着Person的一个实例;会有一个constructor的属性 ;指向Person console.log(p1.constructor == Person5); //true
//好处:可以将它的实例标记为 一种特定的类型;
var person = new Person( 'nic','29','software Engineer' ) person.sayName() //构造fn使用
Person( 'nic','29','software Engineer' ) window.sayName(); //普通fn 属性与方法都加给window对象
var o = new Object(); Person.call(o,'kristen',25,'software Engineer') //另一个对象的作用域中调用 obj.call(o,) this指向o,否则在作用域中,this会指向window;
缺点:每个p1都会有一个sayName()的方法,不是同一个Function的实例,逻辑上讲,构造fn可以定义为:
function Person ( name,age,job ){
this.name = name;
this.age = age;
this.sayName = new Function(){ alert( this.name ) } //与声明是等价的;
}
会导致不同的作用域链和标识符解析不同,不同实例的上的同名函数是不相等的,以下代码可证明这一点:
alert( p1.sayName == p2.sayName2 ) //false;
改进:
function Person ( name,age,job ){ //全局作用域中定义
this.name = name;
this.age = age;
this.sayName = sayname;
}
function sayname(){ alert( this.name ) }
//问题: 全局作用域中定义的fn只能被某个对象调用,名不事实,如果对象需要定义很多方法,则需要定义多个全局fn,无封装性可言了,
//所以,有了原型!
2.3 原型模式
创建的每一个函数都有一个 prototype(原型)属性 [是一个指针,指向一个对象]
用途:包含 可以由特定类型的所有实例共享的属性和方法;
定义 :通过调用构造函数而创建的那个对象实例【比如 p1,是由构造Person创建的对象实例】 的原型对象
好处 :可以让所有对象实例共享它所包含的属性的方法;如p1/p2/p3/p4/.../pn, 所有对象都能共享原型中的属性和方法
function Person(){} //什么都不写
Person.prototype.name = 'nic'
Person.prototype.age = 31
Person.prototype.sayName = function (){ alert(this.name) }
var p1 = new Person();
var p2 = new Person();
p1.sayName(); //nic
p2.sayname();
alert( p1.sayName == p2.sayName2 ) //这次是true了;好处显而易见,不需要重新创建;
2.3.1 理解原型对象
function Person() {} //创建新函数,即会同时建立,fn------>> prototype 属性 ---->>指向函数的原型对象 Person
原型对象获得constructor(构造函数)属性 ---...->>ptototype所在的函数的指针
Person.prototype.constructor ----> person
console.log(Person); //function Person()
console.log(Person.prototype) //Object {name: "nic", age: 31, job: "software Engineer"}
console.log(Person.prototype.constructor); //function Object()
function Person(){}
-----------------------------------------------|
|| |
---------------- |
Person | Person Prototype |
prototype | ------------== constructor --------
---------------- ||
|
--------------- |
创建person1 | |
[prototype] |----------------------
---------------
1、 function Person(){} //创建一个fn,生成一个prototype----------指向---------> Person Prototype (即 2)
2 Person.prototype.name = 'nic'
Person.prototype.age = 31
Person.prototype.sayName = function (){ alert(this.name) }
//以上定义原型属性与方法,生成一个constructor-----------------指向-----------> Person 函数 (即 1)
3、 var p1 = new Person();
var p2 = new Person();
//以上创建实例对象,生成一个 [prototype] --------------指向-------------->Person Prototype(即原型属性和方法 即2)
重写原型之后:
function Person(){}
-----------------------------------------------|
|| |
---------------- |
Person | new Person Prototype |
prototype | ------------== constructor --------
---------------- name nic
age 29
---------------
创建person1 |
[prototype] |--------------== Person Prototype
--------------- constructor
创建person1 中的[ptototype] 重写对象切断了现有原型与任何之前存在的对象实例之间的联系,它们引用的仍然是最初的原型。
----------------------------------------------------------------------------------------------------------------------------------
2.3.2原型in的方法
var zzp1 = new Person();
console.log('in通过原型: '+('name' in zzp1)); //in通过原型或对象都返回true;
zzp1.gg = 'gg'
console.log('in通过对象:'+('gg' in zzp1));
console.log(zzp1.hasOwnProperty(name)); //false; //false 或 not defined;
console.log(zzp1.hasOwnProperty(gg)); // not defined;
结论: 如果hasOwnProperty()方法返回false && in返回true ==== 该属性在原型里
function hasOwnPropertyOrNot(Obj,name){ return !Obj.hasOwnProperty(name) && (name in Obj); }
对象属性可枚举,表示该属性的值不可修改,可认为该属性是常量。
for-in返回所有能通过对象访问、可枚举属性
屏蔽了原型中不可枚举属性的实例属性也会在for-in中返回
所有开发人员定义的属性都是可枚举的
enumerable:true 表示可枚举,即能用for-in显示出来,
enumerable:false 表示不可枚举,即无法用for-in显示出来
----------------------------------------------------------------
var obj2 = {name:'jack',age:23}
Object.defineProperty(obj2,'id',{
value:'123',
enumerable:true //若定义不可枚举只需把 enumerable:false 即可
})
for(var po in obj2){
if(po == 'id'){
alert(' enumerable : true,表示可枚举,如:enumerable : false 表示不可枚举,即不能用for-in显示出来,当然这句也不会显示 ')
}
}
IE8 有bug
获取对象上所有可枚举的属性[代替for-in的方法];
function aap() {
}
aap.prototype.name = 'aap name'
aap.prototype.age = 29;
aap.prototype.job = 'software Engierre'
aap.prototype.sayName = function () {
alert(this.name)
}
var keys = Object.keys(aap.prototype); // 通过原型调用,即fon-in出来的,数组 'name' 'age' 'job' 'sayName'
alert(keys)
var aap1 = new aap();
aap1.name = 'rob'
aap1.age = 30;
var aap1keys = Object.keys(aap1); //通过实例调用
alert(aap1keys) //'name' 'age'
//所有实例办法 :
var keyss = Object.getOwnPropertyNames(aap.prototype);
console.log(keyss); //["constructor", "name", "age", "job", "sayName"]
//包括不可枚举的constructor属性
//可用来代替for-in
2.3.3
function Ps1(){}
var fri = new Ps1(); //重点注意: 先创建实例,后调用重写的原型才出错,如果此句放在重写原型之后则是正常的,不会出错。
Ps1.prototype = {
name:'nnick',
age:29,
job:'software Eng',
sayName:function(){
console.log(this.name);
}
}
重写之后,constructor不再指向Ps1, 相当于完全重写了prototype对象 ,因此constructor变成新对象的constructor属性,指向obj构造fn;
console.log(fri instanceof Object); //true;
console.log(fri.constructor == Ps1); //false;
以下调用都出错:
console.log(fr1.name);
console.log(fr1.age);
console.log(fr1.job)
fr1.sayName(); //is not defined fri指向的原型对象不包含以该名字命名的属性
//调用构造fn时会为实例添加一个指向最初原型的prototype指针;
//原型修改成另一对象 == 切断构造fn 与 最初原型的联系;
//实例中的指针仅指向原型,不指向构造fn;
2.3.5 原生对象的原型
String.prototype.startWith = function(text){
return this.indexOf(text) == 0;
}
var msg = 'hello world!';
alert(msg.startWith('hello')); //true
2.3.6原型对象的问题
引用类型的属性问题
function Ps2(){}
Ps2.prototype = {
constructor:Ps2,
name:'iiiccc',
age:29,
friends:['f1','f2'],
sayName : function () {
console.log(this.name);
}
}
var Ps2_1 = new Ps2();
var Ps2_2 = new Ps2();
Ps2_1.friends.push('van') //本意上想只修改 Ps2_1,
console.log(Ps2_1.friends);
console.log(Ps2_2.friends); //结果调用的Ps2_2 出问题了
2.3.7 组合使用构造fu 原型模式
function ps3(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['f3','f4']
}
ps3.prototype = {
constructor:ps3,
sayName:function(){
console.log(this.name);
}
}
var ps31 = new ps3('p333','31','sfineer')
var ps32 = new ps3('p222','32','sfirry')
ps31.friends.push('van')
console.log(ps31.friends);
console.log(ps32.friends);
console.log(ps31.friends === ps32.friends);
console.log(ps31.sayName === ps32.sayName);
2.3.8 动态原型模式
function ps4(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName != 'function') { //如果没有sayName这个fn,才会将它添加到原型中
ps4.prototype.sayName = function () {
console.log(this.name);
}
}
}
var fr1 = new ps4('ps4name',32,'softEngiir')
fr1.sayName()
2.3.9 寄生构造fn模式
function ps5(name, age, job) { //创建一个新的对象,初始化属性和方法
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
console.log(this.name);
}
return o; //再返回这个对象 ,另构造fn不回返值的情况下,默认会返回新对象的实例。
}
var fr2 = new ps5('fr2name',33,'ef')
fr2.sayName()
function specialArray() {
var values = new Array(); //创建数组
values.push.apply(values,arguments) //添加值
values.toPipedString = function () { //添加方法
return this.join('|')
}
return values; //返回
}
var colors = new specialArray ('red','blue','green');
console.log(colors.toPipedString());
2.3.10 稳妥对象
没有公共属性,不引用this new
与上区别
function Pa1(){
var o = new Object();
o.sayName = function(){
console.log(name);
}
return o;
}
3.1 继承
3.1.2 原型链
function A(){ this.property = true; }
A.prototype.AsayName = function() { return this.Aproperty; } //定义A原型方法
function B(){ this.property = false; }
B.prototype = new A() //继承A 创建A实例,并将该实例赋给B.prototype实现,重写原型对象
B.prototype.BsayName = function (){ return this.Bproperty } //B 继承了A,A继承了object
B.prototype.Asayname = function(){ return false; } //重写A原型 重写超类型的方法
//Asayname 已在原型中出现,如果是 A 调用则是原来的方法,
//如果是用B调用则是重新定义的方法,注意顺序 。
//最后验证
var T = new B();
console.log( T instanceof Object ) //true
console.log( T instanceof A ) //true
console.log( T instanceof B ) //true
3.1.2 原型链的问题
1、引用类型问题
function A(){
this.colors = ['red','yellow','green'] //引用类型的
}
function B() {
}
B.prototype = new A() //B 继承A
var inst = new B();
inst.colors.push('black');
console.log(inst.colors);
var inst2 = new B()
console.log(inst2.colors); //出问题了
2、创建子类,不能向超类型的构造fn中传递参数;
3.1.3 借用构造fn
子类型内部调用超类型的构造fn
function A() {
this.colors = ['red','yellow','green'] //引用类型的
}
function B() {
//继承A
A.call(this); //为下面的var inst = new B();调用A;即会在B对像执行A()函数中定义的所有对象初始化
}
var inst = new B();
inst.colors.push('black');
console.log(inst.colors);
var inst2 = new B()
console.log(inst2.colors); //正常分开,inst2不用被引用到
//传参
function A(name){
this.name = name;
}
function B(){
A.call(this,'nnniiiccc')
this.age = 29;
}
var inst = new B();
alert(inst.name);
alert(inst.age);
-------------- 组合继承-- 伪经典继承------------------------------------------
不足:无论什么情况下,都会计用两次超类型的构造函数,一次是在创建子类型原型的时候,另一次子类型构造函数内部,{子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性}
function A(name) {
this.name = name;
this.colors = ['red','blue','green']
}
A.prototype.sayName = function () {
console.log(this.name);
}
function B(name,age) {
A.call(this,name);
this.age = age;
}
B.prototype = new A() //B的实例赋给A的原型
B.prototype.sayAge = function () {
console.log(this.age);
}
var inst = new B('nnic',29)
inst.colors.push('black');
console.log(inst.colors); //["red", "blue", "green", "black"]
inst.sayName() //nnic
inst.sayAge(); //29
var inst2 = new B('grey',28)
console.log(inst2.colors); //["red", "blue", "green"]
inst2.sayName() //grey
inst2.sayAge() //28
--------------------原型式继承-----------------------------------------------------
function object(o) {
function F() { //创建临时构造Fn
}
F.prototype = o; //传入的对象作为临时构造fn的原型
return new F(); //再返回空上临时类型的一个实例
} //本质上对传入的对象进行浅复制
var person = {
name:'nichol',
friends:['f1','f2','f3']
};
var anotherPerson = object(person) //person传给object对象,返回:新对象,将person作为原型, person.friends ===person所有 也会被下面的共享
anotherPerson.name = 'grey'
anotherPerson.friends.push('rob')
var yetanotherperson = object(person)
yetanotherperson.name = 'yet'
yetanotherperson.friends.push('yetpush')
console.log(person.friends);//["f1", "f2", "f3", "rob", "barbie"]
与Object.create()方法一样
var person = {
name:'nichol',
friends:['f1','f2','f3']
};
var anotherPerson = Object.create(person,{
name:{
value:'grey'
}
})
console.log(anotherPerson) //grey
----------------------寄生式继承------------------------------------------
function A(name){
this.name =name;
this.colors = [ 'red','blue','yellow'];
}
A.prototype.sayName = function(){ console.log(this.name) };
function B(name,age){ A.call(this.name); this.age = age; } // 第二次调用
B.prototype = new A(); //第一次调用
B.prototype.constructor = B;
B.prototype.sayAge = function (){ console.log(this.age) }
function inheritPrototype(B,A) {
var prototype = Object(A.prototype) //创建对象 创建越类型原型的副本
prototype.constructor = B; //增强对象 添加constructor属性,弥补因重写原型失去的默认constructor属性
B.prototype = prototype //指定对象 新创建对象(即副本) 赋值给子类型的原型
}
// function inheritPrototype(子类型构造函数, 超类型构造函数)
7、函数表达式
function AA(i,n,k) {}console.log(AA.name); //AA
function jc(num){ if(num<=1){return 1;}else{ return num * jc(num-1) } } console.log( jc(4); )//递归阶乘
function jc(num){ if(num<=1){return 1;}else{ return num * arguments.callee(num-1) } } console.log( jc(4); )//递归阶乘 arguments.calle 表示一个
指向正在执行的函数的指针;
推荐写法:( 命名函数表达式 )
var factorial = (function f(num) {
if (num <= 1) {
return 1;
}
else {
return num * f(num - 1);
}
})
不影响:
var anotherjc= factorial;
factorial = null; //清空变量
console.log(anotherjc(4));
7、2 闭包
闭包指有权访问另一个函数作用域中的变量的函数。 创建闭包常见的方式:一个函数内部创建另一个函数
function ccf(propertyName){
return function (obj1,obj2){ //匿名函数作用域链中包含ccf()的作用域;
var v1 = obj1[propertyName]
var v2 = obj2[propertyName]
if(v1<v2){
return -1;
}
else if(v1>v2){
return 1;
}
else{
return 0;
}
}
}
function a() {
var i=0;
function b() { //1/函数a的内部函数b
console.log(++i);
}
return b //这里是重点
}
var c = a() //2、被函数a的外部变量引用时,就创建了一个闭包
c()
闭包:a执行完后,javascript垃圾回收机制GC不会收回a所占用的资源 [ why ? 因为a的内部函数b的执行需要依赖a中的变量 ]
a始终存在,再执行c()时,i会自动+1
function a() {
var i=0;
function b() { //1/函数a的内部函数b ab只是相互引用,不受外界打扰,ab会被GC回收
console.log(++i);
}
// return b 这里b没有返回,只是引用
}
var c = a() //2、TypeError: c is not a function
c()
-----------------------------闭包微观世界----------------------------------
概念: 函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)
function a() { //js解析器会将a() 的作用域链(scope chain) == a时a所在的 '环境' 如果a()是全局,则(scope chain) 只有window对象
var i=0;
function b() { //
console.log(++i);
}
return b //
}
var c = a() // 执行a(),进入执行环境(excution context) ,创建执行环境过程: 1、a.scope = a (为a加scope属性,即a的作用域)
2、创建活动对象( call object ) (不具原型不能通过js代码访问);
3、活动对象( call object ) 添加到 a的(scope chain) 最顶端,此时a的(scope chain)
有两个对象: a的(call object) 和 window 对象
4、活动对象( call object ) 添加 arguments 属性 保存调用a所传的参数
5、再把a() 的形参和内部b()的引用也添加到a的 ( call object )上 完成b()定义
因此,函数b的作用域链被设置为b所被定义的环境,即a的作用域。
c()
a 返回 b的引用 给c , 又b的作用域包含对函数a的活动对象引用,也就是说b可以访问到a中定义的所有变量和函数 ,函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。
call object call object
实参arguments 实参arguments
形参parameters b的prototype原型对象 形参parameters windows对象
内嵌function 内嵌 function
内部变量variables 内部变量variables
理解作用域链中包含哪些活动对象??
7.2.1 闭包与变量
闭包只能取得包含函数中任何变量的最后一个值,
function cf(){
var result = new Array();
for( var i=0;i<10;i++ ){
result[i] = function(){
return i;
}
}
return result;
}
var funcs = cf();
for(var i=0;i<funcs.length;i++){
console.log(funcs[i]());
}//每次返回都是10 ,因为每个函数的作用域链中都包含着cf()函数的活动对象,所以它们引用的都是同一个变量i。当cf()函数返回后,变量i的值就是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数返回后都是10
function cf(){
var result = new Array();
for( var i=0;i<10;i++ ){
result[i] = function(num){ //没有直接赋值给数值;定义一个匿名fn;
return function () {
return num; //有创建并返回一个访问 num的闭包,即result数组中每个函数都有自己num变量的副本,因此可以返回不同的值
}
}(i) //传入变量i 会把奕量i的当前值给num;
}
return result;
}
var funcs = cf();
for(var i=0;i<funcs.length;i++) {
console.log(funcs[i]());
}
7.2.2 this 变量
var name = 'the window'
var obj = {
name: 'my ojb',
getNameFun:function () {
return function () {
return this.name;
}
}
}
console.log(obj.getNameFun()()); // the window getnamefun是返回一个函数,因此调用getNamefunn()()会立即调用它返回的函数,结果就是一个字符串,
//为什么没有取得其包含作用域或外部作用域的this对象呢;?
//每个函数被调用时,其活动对象都会自动取得这两个特殊变量,this arguments ,内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个
//外部作用域中的this对象保存在一个闭包里,即能访问到变量里,就可以让闭包访问该对象
var name1= 'the windows'
var obj2 = {
name : 'my obj',
getNameFun: function () {
var that = this; //obj2中的this保存在这里
return function () {
return that.name
}
}
}
console.log(obj2.getNameFun()()); //my obj;
var name2 = 'this window3'
var obj3 = {
name2:'my obj3',
getName: function () {
return this.name2;
}
}
console.log(obj3.getName()); //my obj3
console.log( (obj3.getName)() ); // my obj3
console.log( (obj3.getName = obj3.getName)() ); //the window
7.2.4 内存泄漏 IE
function ag(){
var elem = document.getElementById('someElement')
elem.onclick = function(){
alert(elem.id)
}
}
解决
function ag(){
var elem = document.getElementById('someElement')
var id = elem.id
elem.onclick = function(){
alert(id)
}
elem = null;
}
7.3 私有变量
访问私有的公有方法称为 特权方法 privileged method 两种对像上创建
1、构造函数中定义
function Myobj(){
var privateVariable = 10 //私有变量
function privateFun() { //私有方法
return false;
}
this.publicMethod = function (){ //特权方法
privateVariable++; //访问私有变量
return privateFun(); //调用私有方法
}
}
example:
function Person(name){
this.getname = function (){ //特权方法
return name;
}
this.setname = function (value){ //特权方法
name = value;
}
}
(老话题,每次调用时都会生重新创建这两个方法 ,在构造函数中定义特权方法也有一个缺点:必须使用构造函数模式来达到这个目的),所以使用静态私有变量来实现特权方法即可避免这个问题
7.4.1静态私有变量
(function (){ //创建私有作用域------------注意:1、使用函数表达式,不使用函数声明,函数声明只能创建局部函数
var privateVariable = 10 //私有变量
function privateFun() { //私有方法
return false;
}
Myobj = function(){ //---------------同理,在此,2、声明myobj时也不使用var 否则会变成一个局部函数
}; //构造函数
//公有特权方法 体现原型模式
myobj.prototype.publicMethod = function (){ //特权方法
privateVariable++; //访问私有变量
return privateFun(); //调用私有方法
}
})()
(function () {
var name = '' //2 变成静态,由所有实例共享的属性,
Person = function (value) { //1 构造fn与getname() setname()方法一样,都有权访问私有变量name
name = value;
};
Person.prototype.getName = function () {
return name;
};
Person.prototype.setName = function (value) {
name = value;
};
})();
var p1 = new Person('nicnewp1');
alert(p1.getName());
p1.setName('setnamep1'); //3 在一个实例上调用setname() 会影响所有实例
alert(p1.getName());
var p2 = new Person('michwp2');
alert(p2.getName());
alert(p2.getName());
7.4.2 模块模式
前面:为自定义类型创建私有变量和特权方法
模块模式: 为单例创建私有变量和特权方法 ,只有一个实例的对象 js以对象字面量的方式来创建单例对象
var singleton = {
name:value,
method:function(){
}
}
var singleObj = function () { //1 返回对象的匿名函数
//2 私有变量和私有函数
var privateVariable = 10;
function privateFun() {
return false;
}
//公有方法和属性
return{ //返回的对像只有公有函数属性和方法
publicProperty:true,
publicMethod : function () {
privateVariable++;
return privateFun();
}
}
}()
----------------------------------------
var application = function () { //管理组件的application对象
// 私有变量和私有函数
var components = new Array();
// 初始化
components.push(new Basecomponent());//向数组中添加Basccomponent新实例
return {//返回对象的get...regi..方法
getComponentCount: function () {
return components.length;
},
registerComponent: function (component) {
if(typeof component == 'object'){
components.push(component)
}
}
}
}()
//必须 创建obj,需要初始化,还有公开一些访问私有数据的方法,可使用模块模式;单例作为全局对象存在,不会将它传递给一个fn,
7.4.3 增强的模块模式
var singleObj = function () { //1 返回对象的匿名函数
//2 私有变量和私有函数
var privateVariable = 10;
function privateFun() {
return false;
}
//创建对象
var object = new CustomType(); //返回的对象之前加入对其增强的代码,适合那些单例必须是某种类型的实例 同时必须添加某些属性 方法对其加以增强的情况
//添加特权公有方法和属性
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFun();
}
return object; //再返回这个对象
}();
总结: 函数表达式,拉姆达函数 不需要名字,匿名函数
无法确定如何引用函数时,递归函数全变得比较复杂
递归应始终使用arguments.callee来调用自身,不要用函数名
当函数内部定义了其它函数时,就创建了闭包
后台执行时,闭包的作用域链包含自己、函数、全局作用域
通常,函数的作用域、所有变量都会在函数执行结束后销毁
但,当函数返回一个闭包时,此函数的作用域链接将一直存在内存中直到闭包不存在为止
创建并立即调用一个函数,可执行其中代码,又不会在内在中留下引用
结果即函数内部所有变量都会被销毁,除非将某些变量给包含作用域中的变量
闭包还可用于在对象中创建私胡变量
即使js中没有正式的私有对象概念,但可使用闭包实现公有方法 【此方法可访问包含作用域中的定义变量】
console.log(box.nodeName);
console.log(box.nodeType);
console.log(box.nodeValue);
console.log(box.nodeList);
var mylist = document.getElementsByTagName('ul')[0]
var deeplist = mylist.cloneNode(true);
console.log(deeplist);
console.log(mylist.cloneNode());
console.log(mylist.children[0].nextElementSibling);
console.log(mylist.children[0].previousElementSibling);
console.log(mylist.children[0] == mylist.children[mylist.children.length - 1]);
console.log(mylist.childNodes[0].nodeType);
console.log(mylist.hasChildNodes());
console.log(mylist.getElementsByTagName('li')[0].hasChildNodes());
console.log(mylist.ownerDocument == window.document);
var html = document.documentElement;
console.log(html == document.childNodes[0]);
console.log(html == document.firstChild);
console.log(document.URL);
console.log(document.domain);
console.log(document.referrer);
console.log(window.location.href);
console.log(box.id);
console.log(box.className);
console.log(box.title);
console.log(box.lang);
console.log(box.dir);
html5 规范 自义定 data-
<input type='text' data-username='elmoo' />
getAttribute
setAttribute
attributes
inp.attributes.getNamedItem('id').nodeValue = 'inp2';
console.log(inp.attributes.getNamedItem('id').nodeValue+'123');
console.log(inp.attributes.item(0));
document.createElement()
不能动态设置<iframe> name特性
不能reset()方法重置
动态创建相同name的单选按钮毫无联系
var txt1 = document.getElementById('txt1')
console.log(txt1.childNodes[0].nodeType);
console.log(txt1.childNodes[0].nodeValue);
var txt1 = document.getElementById('txt1')
console.log(txt1.childNodes[0].nodeType);
console.log(txt1.childNodes[0].nodeValue);
var txtNode = document.createTextNode(" 1 、hello")
var antxtNode = document.createTextNode(' 2、anther yippe')
txt1.appendChild(txtNode);
txt1.appendChild(antxtNode)
//合并文本节点,3---> 1
txt1.normalize();
//分割文本节点,splitText(12)从第12位开始;
var newNode = txt1.childNodes[0].splitText(12)
console.log('分割一: ' + txt1.childNodes[0].nodeValue);
console.log('分割二:' + txt1.childNodes[1].nodeValue);
//在文本中间插入数据
var newtxtN = document.createElement('strong')
var newtxtV = document.createTextNode('oooooo')
newtxtN.appendChild(newtxtV);
txt1.insertBefore(newtxtN, txt1.childNodes[1])
var ii = 0;
for (var i = 0; i < txt1.childNodes.length; i++) {
if (txt1.childNodes[i].nodeType == 3) {
ii++
}
}
console.log('总文本节点个数: ' + ii);
var table = document.createElement('table')
var tbody = document.createElement('tbody')
table.appendChild( tbody )
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild( document.createTextNode('c1c1c1c') )
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild( document.createTextNode('c12c1c12') )
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild( document.createTextNode('c3c13c3') )
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild( document.createTextNode('c4c4c4c4') )
NodeList NameNodeMap HTMLCollection三个集合都是动态存在,每当文档结构发生变化时,它们都会得到更新,因此,它们始终保存着最新,最准确的信息;
三者都是类数组
HTMLCollection:返回HTMLCollection类型集合,如document.forms document还有images, applets, links, forms, anchors也返回HTMLCollection类型等
NodeList:节点集合
NamedNodeMap:特性集合
var boxs = document.getElementsByTagName('div') //HTML集合
console.log(boxs instanceof HTMLCollection);
var boxchild = box.childNodes;//节点
console.log(boxchild instanceof NodeList);
var attrs = box.attributes;//特性
console.log(attrs instanceof NamedNodeMap);
DOM扩展
1、选择符API
1、1 Selectors API 支持CSS查询
querySelector()[匹配第一个找到的元素] querySelectorAll() [返回NodeList集合实例]
matchesSelector() 方法 支持较少
1、2元素遍歷
obj.childElementCount
obj.firstElementChild
obj.lastElementChild
obj.previousElementSibling
obj.nextElementSibling
1、3HTML5
var cla = document.getElementsByClassName('cla');
console.log(cla.length);
var box = document.getElementById('box');
var classname = box.className.split(' '); //拆分成數組
var pos = -1;
var i;
var len;
for(i=0,len=classname.length;i<len;i++){
if(classname[i] == 'box1'){
pos = i; //找到匹配則停止搜索,此時i在匹配的位置上
break;
}
}
classname.splice(i,1); //刪除i位置開始1個值
box.className = classname.join(' ');
屬性 classList 是 新集合類型 DOMTokenList 的 實例
DOMTokenList有一個表示自己包含多少元素的length屬性,
add(value);
contains(value);
remove(value);
toggle(value);
前面一大堆代碼可用一句話替代 :box.classList.remove('box1'); //IE10+
1、3、2 焦點管理
activeElement輔助管理DOM焦點功能
document.activeElement();
hasFocus() 確定文檔是否獲得了焦點
var button = document.getElementById('mybutton');
button.focus(); //調用focus方法
console.log(document.activeElement === button); // true 活動指向buton
console.log(document.hasFocus()); //true 文檔獲得焦點
1、3、3 HTMLDocument的變化
readyState屬性
loading;
complete;
是否文檔加載完成?
if(document.readyState == 'complete'){
//
}
自從IE6 區分薰染頁面是 標準 還是 混雜,檢測頁面的兼容性
在標準模式下:document.compatMode == CSS1Compat
在混雜模式下:document.compatMode == BackCompat
if(document.compatMode == 'CSS1Compat'){
alert('standards mode')
}else{
alert('quirks mode')
}
if (document.compatMode == “BackCompat”) {
cWidth = document.body.clientWidth;
cHeight = document.body.clientHeight;
sWidth = document.body.scrollWidth;
sHeight = document.body.scrollHeight;
sLeft = document.body.scrollLeft;
sTop = document.body.scrollTop;
}
else { //document.compatMode == “CSS1Compat”
cWidth = document.documentElement.clientWidth;
cHeight = document.documentElement.clientHeight;
sWidth = document.documentElement.scrollWidth;
sHeight = document.documentElement.scrollHeight;
sLeft = document.documentElement.scrollLeft == 0 ? document.body.scrollLeft : document.documentElement.scrollLeft;
sTop = document.documentElement.scrollTop == 0 ? document.body.scrollTop : document.documentElement.scrollTop;
}
head屬性
document.body屬性 document.head屬性
var head = document.head || document.getElementsByTagName('head')[0]
1、3、4字符集屬性
alert(document.charset)
alert(document.defaultCharset)
自定義字符屬性
data-
dataset屬性取得
<div id='mydiv' data-name='divname'>
var _mydiv = document.getElementById('mydiv')
var _name = mydiv.dataset.name;
1、3、5 插入標記
div.innerHTML = '<script> alert('hi')<\/script>' 無效 script 無作用域
不支持innerHTML :col colgroup frameset head html style table tbody thead tfoot tr title[ie8-]
ie8 window.toStaticHTML方法(); 從源HTML中刪除所有腳本節點和事件處理程序;
1、3、6 outerHTML屬性【注意與innerHTMl區別】
var content = document.getElementById('content')
alert(content.outerHTML); // 得到整個content內容,包括content本身
content.outerHTML = '<p>pp</p>' //改寫整個content內容,包括content本身 ff7之前版本不支持
insertAdjacentHTML()方法
//content.insertAdjacentHTML(要插入位置,要插入的HTML文本)
div1.insertAdjacentHTML('beforebegin','<p>beforebegin</p>') //相鄰前插入
div1.insertAdjacentHTML('afterbegin','<p>afterbegin</p>') //第一個子元素之前插入
div1.insertAdjacentHTML('beforeend','<p>beforeend</p>') //最後子元素之後插入
div1.insertAdjacentHTML('afterend','<p>afterend</p>')//相信後插入
結果:
<p>beforebegin</p>
<div id="div1">
<p>afterbegin</p>
<p>div1</p>
<p>beforeend</p>
</div>
<p>afterend</p>
1、3、6 性能:
for(var i= 0;i<values.length;i++){
ul.innerHTML +='<li>'+values[i]+'</li>'
}
不可取
1、3、7 scrollintoView() 方法
document.getElementById('box2').scrollIntoView()
默認scrollIntoView(true),元素頂部與窗口對齊
scrollIntoView(false),元素底部與窗口對齊
1、DOM2 DOM3
DOM1: HTML 與 XML 文檔的底部結構
DOM2&DOM3:則在DOM1基礎上引入更多交互能力,也支持更高級的XML特性
var supportDOM2Core0 = document.implementation.hasFeature('core','2.0')
1.1針對XML命名空間
HTML不支持XML命名空間 XHTML支持XML命名空間
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
...
</html>
XML命名空間創建前綴
<xhtml:html xmlns = "http://www.w3.org/1999/xhtml">
<xhtml:head>
...
</xhtml:html>
混合xhtml svg
1.1.1 Node類型變化
localName:不帶命名空間前綴的節點名稱
namespaceURI:命名空間URI或者(未指定情況下是)null
prefix:命名空間前綴
1.1.2 DOcument類型變化
createELementNS
createAttributeNS
...
創建一個SVG元素
var svg = document.createElementNS("http://www.w3.org/2000/svg","svg");
創建一個屬性某個命名空間的新特性
var att = document.createAttributeNS("http://www.somewhere.com","random")
取得所有XHTML
var elems = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","*")
1.1.3 Element類型變化
getAttributeNS(namespaceURI,localName)
getAttributeNodeNS(namespaceURI,localName)
hasAttributeNS
removeAttriubteNS
setAttributeNS
setAttributeNodeNS
1.1.4 NamedNodeMap類型變化
getNamedItemNS
removeNamedItemNS
setNamedItemNS
2.1 訪問元素樣式
var box = document.getElementById('box')
box.style.cssText = 'width:200px;height:100px;background-color:red'
console.log(box.style.cssText);
for (var i = 0, len = box.style.length; i < len; i++) {
value = box.style.getPropertyValue(box.style.item(i))
console.log(box.style.item(i) + ':' + value);
}
移除樣式:
box.style.removeProperty('width')
2.2計算的樣式
style對象,不包含那些從其他樣式表層疊而來並影響到當前元素的樣式信息;
DOM2級樣式,增強document.defaultView,提供 getComputedStyle()方法
方法封裝:
function getStyle(obj,attr) {
if(obj.currentStyle){
return obj.currentStyle[attr];
}
else{
return document.defaultView.getComputedStyle(obj,null)[attr]
}
}
2.3
1) 元素大小
offsetHeight :heigth border padding
offsetWidth :width border padding
offsetLeft
offsetTop
獲取元素在頁面上的偏移量:offsetLeft和offsetTop與其offsetParent相加,循環至根元素;
function getElementLeft(elem) {
var actLeft = elem.offsetLeft;
var current = elem.offsetParent;
while (current !== null) {
actLeft += current.offsetLeft;
current = current.offsetParent
}
return actLeft;
}
2) client大小
document.documentElement.clientWidth document.body.clientWidth
可視區大小方法
function getViewport() {
if (document.compatMode == 'BackCompat') {
return { //混雜模式
width: document.body.clientWidth,
height: document.body.clientWidth
};
}
else {
return { //標準模式
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
}
}
}
3) 包含滾動內容的元素大小,滾動大小,
scrollHeight:無滾動條情況下,元素內容總高
scrollWidth: 無滾動條的情況下,元素內容總寬
scrollLeft: 滚动距离
scrollTop: 滚动距离
确定文档总高度时,必须取得scrollWidth/clientWidth,serollHeight/clientHeight中的最大值,
var docHeight = Math.max( document.documentElement.scrollHeight,document.documentElememet.clientHeight );
var docWidth = Math.max( document.documentElement.scrollWidth,document.documentElement.clientWidht );
IE:document.body..
判断是否居于顶部:
function scrollToTop(elem){
if(elem.scrollTop!=0){
elem.scrollTop = 0;
}
}
obj.getBoundingClientRect()获取座標
right - left = offsetWidth
bottom - top = offsetHeight
2.4遍歷 IE不支持DOM遍歷
IE 冒泡
Netscape Communicator事件
以 on 开头
window.onload = 中 { function s(){alert(this)} }
onclick = "s()",这种形式只能写在上面,window.onload不起作用
var inptry = document.getElementById('inp-try');
inptry.onclick = function () {
try{
s();
}
catch (e){
alert(123)
}
}
//兼容事件绑定及移除事件
var EventUtil = {
addHandler: function (elem,type,handle) {
if(elem.addEventListener){
elem.addEventListener(type,handle,false);
}
else if(elem.attachEvent){
elem.attachEvent('on'+type,function(){
handle.call(elem);
})
}
else (elem['on'+type]) = handle;
},
removeHandle: function (elem, type, handle) {
if(elem.removeEventListener){
elem.removeEventListener(type,handle,false);
}
else if(elem.detachEvent){
elem.detachEvent('on'+type,handle)
}
else (elem['on'+type]) = null;
}
}
obj.onclick = function(e){
e = e || window.evet;
alert(e.type)
}
this始终等于e.currentTarget
目标元素下:this currentTarget/target 相同
doument:currentTarget
li[0].onclick= function (e) {
alert(e.target === this) //true
alert(e.currentTarget === this) //true
}
document.onclick = function (e) {
alert(e.currentTarget===this) //true
alert( e.target === this) //false
}