一、代码模块
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方法使用
function test_func(name,sex){ this.name = name; this.sex = sex; } var xiaoming = {}; test_func.call(xiaoming func,"xiaoming",10); console.log(xiaoming);
(2)apply使用 两个参数 一个是函数的作用域(this),另一个是参数数组.
function test_func(name,sex){ this.name = name; this.sex = sex; } var xiaoming = {}; test_func.apply(xiaoming,["xiaoming",10]); console.log(xiaoming);
3.隐式传递this
这个this就是对象, 也就是对象直接调用,this被隐式传到函数里,
var xiaohong = { name: "xiaohong", test_func: function(){ console.log(this); }, } xiaohong.test_func();
4.强制传递绑定this
强制设置函数体this的值, 参数是一个表.
但是对象却不会引用这个函数>
var func = 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)绑定的优先级 大于 隐式传递
绑定一般用在回调函数。
var xiaohong = { name: "xiaohong", test_func: function(){ console.log(this); }.bind(5), } xiaohong.test_func(); //输出的是5
(2)显示传递 大于 隐式传递
强制 > 显示 > 隐式
三、实现面向对象
.在javascript中不存在类的概念, 而是通过 构造函数
和原型链(prototype chains)实现的
function person(name, sex){ this.name = name; this.sex = sex; } person.prototype.test_func = function(){ console.log("person test_func"); console.log(this); } var p = new person("xiaohong",10); console.log(p); var p2 = new person("xiaotian",12); console.log(p2); p.test_func(); p2.test_func();
构造函数
(1)构造函数提供一个生成对象的模板并描述对象的基本结构,
一个构造函数可以生成多个对象,每个对象都有相同的结构,
构造函数就是对象的模板, 对象就是构造函数的实例,
构造函数特点:
a:内部使用的this对象,指向要生成的对象实例,
b:使用new操作符来调用构造函数,并返回对象实例,
(2)构造函数的缺点: 同一个对象实例之间, 当你new调用构造函数
返回一个实例的时候,里面的所有函数都会新创建出来, 这个函数
都是一个新创建的函数, 是不被实例共享的, 这样很浪费资源.
function 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
prototype属性
(1) 为了解决构造函数的对象之间无法共享属性的缺点,js提供了prototype属性.
(2)js中所有类型都是对象,(除了null和undefined),而每个对象都继承自另一个
对象, 称为"原型"对象 (prototype object) , null没有原型对象,
(3)原型对象上的所有属性和方法,都会被对象实例所享.
(4)通过构造函数生成对象实例时, 会将对象实例的原型指向构造函数
的prototype属性, 每一个构造函数都有一个prototype属性,
这个属性就是对象实例的原型对象,
(5)对于构造函数prototype是作为构造函数的属性,对于对象实例来说
prototype是对象实例的原型对象, 所以prorotype即是属性,又是对象.
function Person(name,height){ this.name=name; this.height=height; } Person.prototype.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);//true
(6)原型对象的属性不是对象实例的属性,对象实例的属性是继承自
构造函数定义的属性,因为构造函数内部有一个this关键字,来指向
要生成的对象实例,对象实例属性, 其实就是构造函数内部定义的属性.
所以只要你修改原型对象上的属性和方法, 变动会离开体现在所有对象实例上.
(7)当某个对象实例没有该属性或方法时, 就会到原型对象上去查找
如果实例对象自身有某个对象或方法, 就不会去原型对象上查找,
如下,当boy对象实例hobby方法修改时, 就不会在继承原型对象
上的hobby方法了, 不会girl没有修改, 所以它依旧继承原型对象的方法.
function Person(name,height){ this.name=name; this.height=height; } Person.prototype.hobby = function(){ console.log("aaa"); } var boy = new Person('keith',180); var girl = new Person('rascal',153); boy.hobby = function(){ console.log("bbb"); } boy.hobby(); //bbb girl.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关键字不是用来创建一个类的实例对象,而是用于继承,
function Animal(name){ this.name = name; } Animal.color = "black"; Animal.prototype.say = function(){ console.log("I'm " + this.name); }; var cat = new Animal("cat"); console.log( cat.name, //cat cat.height //undefined ); cat.say(); //I'm cat console.log( Animal.name, //Animal Animal.color //back ); Animal.say(); //not function
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 这行代码的时候,做了很懂工作,伪代码如下
var obj = {}; obj.__proto__ = Animal.prototype; var result = Animal.call(obj,"cat"); //显示传递this,和参数到构造函数 return typeof result === '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)根据上面的知识实现面向对象
function Point(){ this.xpos = 0; this.ypos = 0; } Point.prototype.set_pos = function(x,y){ this.xpos = x; this.ypos = y; } Point.prototype.get_posy = function(){ return this.ypos; } var p1 = new Point(); var p2 = new Point(); 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.首先定义一个人类
var Person = 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只是共享这个方法属性.
//定义一男人类 var Man = function(){}; //做法(1) //做法(2) var Super = function(){}; Super.prototype = Person.prototype; Man.prototype = new Super(); //这里就是继承 Man.prototype.set_sex = function(sex){ this.sex = sex; } console.log(Super.prototype); console.log(Man.prototype); console.log(Man.prototype.__proto__); var m = new Man(); //使用父类方法 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实现 可以继承 也可以继承
function Class(param){ var new_class = function(){}; //如果有要继承的父类 if(param.extend != null){ var Super = function(){}; Super.prototype = param.extend.prototype; new_class.prototype = new Super(); } //遍历参数 把内容添加到新对象里 for(var key in param){ if(key == "extend"){ continue; } new_class.prototype[key] = param[key]; } return new_class; }
(6)使用Class函数 实现继承和 扩展方法属性
//Student学生类 传入参数表 var Student = Class({ //继承自 Person extend: Person, //定义方法 set_class: function(classa){ console.log("set_class",classa); }, //定义属性 name: "学生类", }); //实例化对象 var s = new Student(); s.set_class(12312); console.log(s.name);
闭包
1. 看一段闭包代码
function a(){ var n = 0; function inc(){ n++; console.log(n); } inc(n); inc(n); } a(); //输出1 输出2
2.再 看一段闭包代码
function a(){ var n = 0; this.inc = function(){ n++; console.log(n); }; } var c = new a(); c.inc(); //1 c.inc(); //2
3.闭包 有权访问另一个函数作用域内的遍历都是闭包, Inc函数访问
构造函数a里面的遍历n,所以形成一个闭包
4.匿名函数,lambda表达式,闭包区别:
从功能行上说他们是一个东西, 只是不同语言的不同称呼罢了,
它们都是匿名函数, 若匿名函数捕获了一个外部变量,它就是闭包.
qq交流群:140066160
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。