一、代码模块

1.js里面代码可以放在不同的文件里, 称为模块

2.一个模块需要引用其他模块代码的时候,使用require()

3.require:

(1)如果是第一次调用,那么就加载,执行脚本

(2)每个代码模块由module.exports导出的对象

(3)每次require的时候,都返回module.exports(模块出口)

他可以指向任何类型, 函数,对象, 值.....

4.requirefunction Person(name,height){
this.name=name;
this.height=height;
this.hobby=function(){
return 'watching movies';
}
}

var boy=new Person('keith',180);
var girl=new Person('rascal',153);
console.log(boy.name); //'keith'
console.log(girl.name); //'rascal'
console.log(boy.hobby===girl.hobby);//false

, 如果是第一次执行,他就把

js里所有代码加载进来, 在执行这整个js文件, 然后返回module.exports.

require得到的就是module.exports指向的对象.

5.如果不是第一次require,他就直接返回module.exports.






二、this


显示传递this call


1.每个函数都包含两个非继承而来的方法:call()方法和apply()方法。

(1)相同点: 两个方法的作用是一样的

(2)不同点: 接收参数的方式不同.

2.作用:

.在特定的作用于中调用函数,等于设置函数体内this对象的值,

扩充函数赖以运行的作用域

(1)call方法使用

functiontest_func(name,sex){this.name=name;this.sex=sex;}varxiaoming={};test_func.call(xiaomingfunc,"xiaoming",10);console.log(xiaoming);

(2)apply使用 两个参数 一个是函数的作用域(this),另一个是参数数组.

functiontest_func(name,sex){this.name=name;this.sex=sex;}varxiaoming={};test_func.apply(xiaoming,["xiaoming",10]);console.log(xiaoming);



3.隐式传递this

这个this就是对象, 也就是对象直接调用,this被隐式传到函数里,

varxiaohong={name:"xiaohong",test_func:function(){console.log(this);},}xiaohong.test_func();




4.强制传递绑定this

强制设置函数体this的值, 参数是一个表.


但是对象却不会引用这个函数>

varfunc=function(){console.log(this);}.bind(xiaohong);func();



5.定义完函数体,在绑定对象。

在定义完函数体后, 这个函数对象的this一定不是绑定的;

因为,函数对象来绑定对象的时候,不是把对象绑定到func函数对象里,

而是产生了一个新的函数对象,然后返回这个函数对象.

(1)错误,没有获得绑定好的函数对象

func=function(){console.log(this);}func.bind(xiaohong);


(2)正确,先获得绑定好的函数对象,再调用

func=function(){console.log(this);}func=func.bind(xiaohong);func();



7.优先级

(1)绑定的优先级 大于 隐式传递

绑定一般用在回调函数。

varxiaohong={name:"xiaohong",test_func:function(){console.log(this);}.bind(5),}xiaohong.test_func();//输出的是5


(2)显示传递 大于 隐式传递

强制 > 显示 > 隐式








三、实现面向对象

.在javascript中不存在类的概念, 而是通过 构造函数

和原型链(prototype chains)实现的

functionperson(name,sex){this.name=name;this.sex=sex;}person.prototype.test_func=function(){console.log("persontest_func");console.log(this);}varp=newperson("xiaohong",10);console.log(p);varp2=newperson("xiaotian",12);console.log(p2);p.test_func();p2.test_func();





构造函数
(1)构造函数提供一个生成对象的模板并描述对象的基本结构,

一个构造函数可以生成多个对象,每个对象都有相同的结构,

构造函数就是对象的模板, 对象就是构造函数的实例,

构造函数特点:

a:内部使用的this对象,指向要生成的对象实例,

b:使用new操作符来调用构造函数,并返回对象实例,



(2)构造函数的缺点: 同一个对象实例之间, 当你new调用构造函数

返回一个实例的时候,里面的所有函数都会新创建出来, 这个函数

都是一个新创建的函数, 是不被实例共享的, 这样很浪费资源.

functionPerson(name,height){this.name=name;this.height=height;this.hobby=function(){return'watchingmovies';}}varboy=newPerson('keith',180);vargirl=newPerson('rascal',153);console.log(boy.name);//'keith'console.log(girl.name);//'rascal'console.log(boy.hobby===girl.hobby);//false




prototype属性

(1) 为了解决构造函数的对象之间无法共享属性的缺点,js提供了prototype属性.


(2)js中所有类型都是对象,(除了null和undefined),而每个对象都继承自另一个

对象, 称为"原型"对象(prototype object) , null没有原型对象,


(3)原型对象上的所有属性和方法,都会被对象实例所享.


(4)通过构造函数生成对象实例时, 会将对象实例的原型指向构造函数

的prototype属性, 每一个构造函数都有一个prototype属性,

这个属性就是对象实例的原型对象,


(5)对于构造函数prototype是作为构造函数的属性,对于对象实例来说

prototype是对象实例的原型对象, 所以prorotype即是属性,又是对象.

functionPerson(name,height){this.name=name;this.height=height;}Person.prototype.hobby=function(){return'watchingmovies';}varboy=newPerson('keith',180);vargirl=newPerson('rascal',153);console.log(boy.name);//'keith'console.log(girl.name);//'rascal'console.log(boy.hobby===girl.hobby);//true

(6)原型对象的属性不是对象实例的属性,对象实例的属性是继承自

构造函数定义的属性,因为构造函数内部有一个this关键字,来指向

要生成的对象实例,对象实例属性, 其实就是构造函数内部定义的属性.

所以只要你修改原型对象上的属性和方法, 变动会离开体现在所有对象实例上.


(7)当某个对象实例没有该属性或方法时, 就会到原型对象上去查找

如果实例对象自身有某个对象或方法, 就不会去原型对象上查找,


如下,当boy对象实例hobby方法修改时, 就不会在继承原型对象

上的hobby方法了, 不会girl没有修改, 所以它依旧继承原型对象的方法.

functionPerson(name,height){this.name=name;this.height=height;}Person.prototype.hobby=function(){console.log("aaa");}varboy=newPerson('keith',180);vargirl=newPerson('rascal',153);boy.hobby=function(){console.log("bbb");}boy.hobby();//bbbgirl.hobby();//aaa


(8).原型链

对象的属性和方法,可能是定义在自身,也可能是原型对象,由于

原型对象本身对于对象实例来说也是对象,它也有自己的原型, 所以形成了

一条原型链(prototype chain),比如a对象是b对象的原型,b对象是c对象的原型

以此类推, 所有对象的原型顶端, 都是object.prototype,即object构造

函数的prototype指向的哪个对象, 而object.prototype也有自己的原型对象,

这个对象就是 "null" 没有任何属性和方法, 而null对象则没有原型.

特点:

a.读取对象某个属性时,javaScript先找对象本身的属性,如果找不到,就去他的原型找,

如果还是找不到,则去原型的原型找,直到object.portotype找不到则返回 undefined.

b.如果对象自身和它的原型定义了同名属性,优先读取对象自身属性,称为 "覆盖"

c.一级级向上找属性, 对性能有影响, 越上层,影响越大,



new 操作符

1.javascript也有new关键字, js中万物皆对象, 为何还要new关键字.

其实js中new关键字不是用来创建一个类的实例对象,而是用于继承,

functionAnimal(name){this.name=name;}Animal.color="black";Animal.prototype.say=function(){console.log("I'm"+this.name);};varcat=newAnimal("cat");console.log(cat.name,//catcat.height//undefined);cat.say();//I'mcatconsole.log(Animal.name,//AnimalAnimal.color//back);Animal.say();//notfunction

2.代码解读

1-3 行创建一个函数Animal,并在其this上定义了属性:name,

4 行在Animal对象(Animal本身是函数对象)上定义了一个静态属性:color,并复制"black"

5-7行在Animal函数的属性prototype上定义一个say方法,say输出了this的name值

8行使用new关键字创建了一个新对象cat,

10-14行cat对象常识方位name和color属性,并调用say方法

16-20行Animal对象访问name和color属性,并调用say方法



3.重点解析 new方法做了什么?

js引擎在执行new 这行代码的时候,做了很懂工作,伪代码如下

varobj={};obj.__proto__=Animal.prototype;varresult=Animal.call(obj,"cat");//显示传递this,和参数到构造函数returntypeofresult==='obj'?result:obj;//

a)__proto__(隐式原型)与prototype(显式原型)

每个对象都有__proto__属性, 但只有函数对象才有prototype属性.

b ) 首先会创建一个空对象obj, 把obj的__proto__指向Animal的原型对象

prototype, 此时便简历了obj对象的原型链:

obj -> Animal.prototype -> object.prototype -> null

c)在obj对象执行空间调用构造函数并传递参数"cat"

这里这个obj就是构造函数里的this对象,this指向的就是obj;

d ) 判断返回值 如果无返回值或者返回非对象值, 则将obj作为新对象, 否则返回值

坐位新对象,



4.了解new运行机制后,cat就是d过程的返回值 表, 他有一个原型链,

cat上新增了一个新的属性: name


5.再次参照上面的代码分析

(1)cat.naem 在过程c 中obj作为this,传递到构造函数,构造产生name属性,

因此cat.name就是obj.name

(2)cat.color cat首先在自身找color,没有则言责原型链查找,我们仅在Animal

对象是哪个定义color,没有再其原型链上定义, 因此找不到

(3)cat.say() 首先找自身,没有,找原型链Animal的prototype属性定义了say

因此可以再原型链是上找到say方法

(4)Animal.color 对于Animal来说它本身是一个对象,因此他在访问属性也遵守

上述查找规则, 所以他能找到

(5)Animal.name 先查找自身naem 但这个name不是我们定义的name,

而是这个函数对象内部本身就存在这个属性,一般情况下,函数对象在产生内置name

属性会将函数名作为赋值(仅函数对象).

(6)Animal.say() Animal在自身查找, 没有,沿着原型链查找,

这是Animal函数对象的原型链

Animal的原型对象是Function.prototype

原型链: Animal -> Function.prototype -> Objecct.prototype -> null

在Animal原型链上并没有找到,say方法, 因为Animal的prototype

只是Animal的一个属性, 并不是它的原型对象,

Animal的原型对象是Function.prototype

(7)所有实例化的对象,他们的__protp__都指向构造函数的prototype属性

只要有一个实例对象修改其中的内容, 其他的实例对象也会跟这改变.

(8)根据上面的知识实现面向对象

functionPoint(){this.xpos=0;this.ypos=0;}Point.prototype.set_pos=function(x,y){this.xpos=x;this.ypos=y;}Point.prototype.get_posy=function(){returnthis.ypos;}varp1=newPoint();varp2=newPoint();p1.set_pos(10,20);p2.set_pos(100,200);console.log(p1.get_posy());console.log(p2.get_posy());




类的继承(原型实现)

1.继承就是父类有的方法或属性, 子类继承后其方法和属性子类也可以使用的.

2.继承实现方法: A类有个prototype, 类B也有个prototype

把类A的prototype的内容 复制(浅复制) 给类B的prototype,

这样的话,类A和类B公用了这个prototype

3.首先定义一个人类

varPerson=function(){};Person.prototype.set_name=function(name){this.naem=name;//设置姓名};Person.prototype.set_age=function(age){this.age=age;//设置年龄};

4.定义一个男人类, 继承自人类, 有两种做法

(1)男人类.prototype = Person.prototype;

但是这样会造成一个问题:这样赋值就是引用赋值 (浅赋值),

导致这两个prototype指向的是同一个对象, 如果子类修改一个

方法属性, 父类的也会跟着改变, 显然是不行的.


(2)new出来的是新的实例,会把原来的prototype拷贝过来

这样就可以扩展自己的方法了, 实际上这个prototype的__ptoto还是

指向父类的prototype, 而prototype则共享__ptoto__里的属性方法,

扩展子类prototype的时候,不影响父类ptototype, 应为是new出来的, 他的

__proto__才指向父类prototype, ptototype只是共享这个方法属性.

//定义一男人类varMan=function(){};//做法(1)//做法(2)varSuper=function(){};Super.prototype=Person.prototype;Man.prototype=newSuper();//这里就是继承Man.prototype.set_sex=function(sex){this.sex=sex;}console.log(Super.prototype);console.log(Man.prototype);console.log(Man.prototype.__proto__);varm=newMan();//使用父类方法m.set_name("小王");m.set_age(10);//使用子类方法m.set_sex(0);console.log(m);


(3) 只要你扩展自己方法,和父类同名,就会隐藏父类方法,

因给根据原型链原则, 查找方法属性 先到实例本身进行查找 然后原型对象

这个原型其实就是他的父类.

Man.prototype.set_name=function(name){console.log("子类重写该方法");}

(4) 如果要调用父类的函数, 使用父类的ptototype 把子类实例传给父类方法

Man.prototype.set_name=function(name){person.prototype.set_name.call(name);console.log("子类重写该方法");}



(5)Class实现 可以继承 也可以继承

functionClass(param){varnew_class=function(){};//如果有要继承的父类if(param.extend!=null){varSuper=function(){};Super.prototype=param.extend.prototype;new_class.prototype=newSuper();}//遍历参数把内容添加到新对象里for(varkeyinparam){if(key=="extend"){continue;}new_class.prototype[key]=param[key];}returnnew_class;}


(6)使用Class函数 实现继承和 扩展方法属性

//Student学生类传入参数表varStudent=Class({//继承自Personextend:Person,//定义方法set_class:function(classa){console.log("set_class",classa);},//定义属性name:"学生类",});//实例化对象vars=newStudent();s.set_class(12312);console.log(s.name);



闭包

1. 看一段闭包代码

functiona(){varn=0;functioninc(){n++;console.log(n);}inc(n);inc(n);}a();//输出1输出2

2.再 看一段闭包代码

functiona(){varn=0;this.inc=function(){n++;console.log(n);};}varc=newa();c.inc();//1c.inc();//2

3.闭包 有权访问另一个函数作用域内的遍历都是闭包, Inc函数访问

构造函数a里面的遍历n,所以形成一个闭包


4.匿名函数,lambda表达式,闭包区别:

从功能行上说他们是一个东西, 只是不同语言的不同称呼罢了,

它们都是匿名函数, 若匿名函数捕获了一个外部变量,它就是闭包.




qq交流群:140066160