面向对象
面向对象特点
什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部细节
创建对象的方法:
var arr = new Array();系统对象
var obj = new object();创建的是空对象
面向对象编程的特点:
1,抽象:抓住核心问题
2,封装:只能通过对象来执行方法(不考虑内部实现,只考虑功能使用)
3,继承:从已有对象上继承新的对象 多重继承
4,多态:多个对象的不同形态
对象的组成:
1,方法 - 函数:过程,动态的
2, 属性 - 变量:状态,静态的
继承对象:
1,方法
2,属性
用new去调用一个函数的时候,
这个函数创造的对象就是this
这个所调用的函数,默认返回对象
this : 当前的方法属于谁
原型:
prototype ,只能给构造函数加
去改写对象下面的方法或者属性,为了让公用的方法或者属性不去重写
var arr = new Array();
Array 类:不具备实际的功能,只能用来构造对象
arr 对象:真正有功能的东西,被类给构造出来
原型比较重要的功能:可以扩展系统对象
类:构造函数就是类
在构造函数里面添加的是属性,在原型里面添加的是方法
构造函数语法:
function 函数名(){
this.属性=^;
};
函数名.prototype.方法=function(){};
var obj= new 构造函数();
obj.方法();
面向对象常用的属性和方法
hasOwnproprety(): 看看是不是对象自身下面的属性
constructor: 查看对象的构造函数 ,这是个属性
instanceof :对象于构造函数在原型链上是否有关系
toString: 把对象转成字符串
toString在系统对象下面都是自带的,自己写的对象都是通过原型链找object下面的
object.prototype.toString.call(obj);//判断对象的类型
包装对象:
包装对象: 基本类型都有自己对应的包装对象。
字符串的包装对象是String,数字的包装对象是Number,布尔的包装对象是Boolean。
高级面向对象
原型链:
实现对象和原型之间的连接
_proto_
hasOwnproperty(); 是不是对象自己的属性;返回布尔值;
constructor :属性,值是构造函数;
(当new一个对象的时候,原型里面默认带有很多的属性和函数的,并且不可以for in)
instanceof :判断构造函数是否一样;
toString(); 把对象变成字符串
继承:
子级继承父级;
子级可以用父级的方法和属性;
子级的改变不影响父级;
person.call(this,name);
this 是新的对象,name是原构造函数第一个参数
什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部细节
面向对象
什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部细节
面向对象编程的特点:
抽象:抓住核心问题
封装:不考虑内部实现,只考虑功能使用
继承:从已有对象上,继承出新的对象
多重继承
多态
原型:
prototype
var arr = new Array();
Array 类:不具备实际的功能,只能用来构造对象
arr 对象:真正有功能的东西,被类给构造出来
原型比较重要的功能:可以扩展系统对象
类:构造函数就是类
在构造函数里面添加的是属性,在原型里面添加的是方法
程序改写成面向对象的程序:
1,前提:所有东西都在onload里面
2,改写:不能有函数嵌套,可以有全局变量
3, onload - 构造函数
全局变量 - 属性
函数 - 方法
对象的组成:
1,属性
2,方法
继承对象:
1,属性
2,方法
继承:先执行父级的构造函数,然后再添加子类的属性。
继承玩的就是原型链
JS 实现继承的几种方式
1.原型链继承
---------两个实例使用的是同一个原型对象。它们的内存空间是共享的,当一个发生变化的时候,另外一个也随之进行了变化,这就是使用原型链继承方式的一个缺点
function Parent1() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child1() {
this.type = 'child2';
}
Child1.prototype = new Parent1();
console.log(new Child1());
let s1 = new Child1();
let s2 = new Child2();
s1.play.push(4);
console.log(s1.play, s2.play);
2.构造函数继承(借助 call)
-------这样写的时候子类虽然能够拿到父类的属性值,解决了第一种继承方式的弊端,但问题是,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法。这种情况的控制台执行结果如下图所示。
function Parent1(){
this.name = 'parent1';
}
Parent1.prototype.getName = function () {
return this.name;
}
function Child1(){
Parent1.call(this);
this.type = 'child1'
}
let child = new Child1();
console.log(child); // 没问题
3.组合继承(前两种组合)
这种方式结合了前两种继承方式的优缺点,结合起来的继承–通过注释我们可以看到 Parent3 执行了两次,第一次是改变Child3 的 prototype 的时候,第二次是通过 call 方法调用 Parent3 的时候,那么 Parent3 多构造一次就多进行了一次性能开销,这是我们不愿看到的
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
Parent3.prototype.getName = function () {
return this.name;
}
function Child3() {
// 第二次调用 Parent3()
Parent3.call(this);
this.type = 'child3';
}
// 第一次调用 Parent3()
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
4.原型式继承
通过 Object.create 这个方法可以实现普通对象的继承,不仅仅能继承属性,同样也可以继承 getName 的方法,请看这段代码的执行结果。
----------那么关于这种继承方式的缺点也很明显,多个实例的引用类型属性指向相同的内存,存在篡改的可能,接下来我们看一下在这个继承基础上进行优化之后的另一种继承方式——寄生式继承。
let parent4 = {
name: "parent4",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
let person4 = Object.create(parent4);
person4.name = "tom";
person4.friends.push("jerry");
let person5 = Object.create(parent4);
person5.friends.push("lucy");
console.log(person4.name);
console.log(person4.name === person4.getName());
console.log(person5.name);
console.log(person4.friends);
console.log(person5.friends);
5.寄生式继承
使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。----虽然其优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。
let parent5 = {
name: "parent5",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
function clone(original) {
let clone = Object.create(original);
clone.getFriends = function() {
return this.friends
};
return clone;
}
let person5 = clone(parent5);
console.log(person5.getName());
console.log(person5.getFriends());
6.寄生组合式继承
结合第四种中提及的继承方式,解决普通对象的继承问题的 Object.create 方法,我们在前面这几种继承方式的优缺点基础上进行改造,得出了寄生组合式的继承方式,这也是所有继承方式里面相对最优的继承方式,代码如下。
function clone (parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child5';
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());
面向对象开发:把全局变量变成属性,属性和执行事件放进构造函数里面,把函数方法放在原型里面,创建的对象放在onload里面。
this容易出问题的两个地方:一个是事件,一个是定时器。解决方法:套一个函数,在外面存一个需要用到的this变量,然后在函数里面调用这个this变量去做事情,就解决问题。
命名空间:用Json对象的方式,来分别存放不一样的东西,要注意的是,东西只能用一次。
命名空间的语法:
var json ={};
json.hover={
‘over’:‘onmouseover’,
'out':'onmouseout'
};
json.down={
'don':'onmousedown',
'up':'onmouseup'
};
面向对象的继承用到的属性和方法:
function Work(name,sex,job){
Person.call(this,name,sex);//调用父级的构造函数,来继承属性
this.job=job;//添加自己的新属性
};
//继承父级的原型(方法)
for(var i in Person.pototype){
Worker.pototype[i]=Person.pototype[i];//拷贝父级方法
};
Worker.pototype=Person.pototype;//引用父级的原型,子级的原型修改会影响父级的原型。
调用父级的构造函数,为了继承属性:person.call(this,name,sex);
原型链:for in循环父对象的原型,然后把父对象原型的东西塞进当前对象原型的里面 //通过原型来继承父级的方法
引用:只有对象才能引用,引用指的是内存空间的地址相同,切更改任意一个会变动整个对象
instance of : 判断对象(实例)里面是否有这个原型,如果有就返回true,没有就false。
继承的其它方式:
类式继承:
类:Js当中没有类的概念,把Js中的构造函数看作的类
创建一个新的空函数,
把空函数的prototype 等于 父对象的prototype
子对象.prototype = new 新的空函数();
子对象.prototype.constructor = 子对象; 修正指向问题
原型继承:
系统对象: Dom,Bom
宿主:生活环境
Math:不需要实例化(new),直接可以使用–静态对像。
本地对象:
原型链
原型链:实例对象与原型之间的链接,叫做原型链。
原型链最外层:object.prototype
proto 隐形式链接
原型链
实现对象和原型之间的连接 (主要参考 图1 )
_proto_ 隐形式链接
原型链最外层:object.prototype
proto 隐形式链接
a:读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
b:如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。
c:一级级向上在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。
每个函数都拥有一个prototype属性,每个函数实例对象都拥有一个__proto__属性,而这个属性指向了函数的prototype,当我们访问实例对象的属性或者方法时,会先从自身构造函数中查找,如果没有就通过__proto__去原型中查找,这个查找的过程我们称之为原型链。
1.prototype的原型对象
(Object.prototype是一个对象,用于表示Object的原型对象
几乎所有的Javascript的对象都是Object的实例)
_fn函数才有的属性
2.函数的_proto_ 都指向Function.prototype
3.proto 内部原型
_{} 对象才有的属性
4.对象由函数(构造器)生成
对象的 _proto_属性
指向构造函数的prototype属性
即:_proto_表示的是,实例与(构造函数的原型)之间的关系
图1:
js内置对象
扩展
随机函数
Math.round(); 四舍五入
Math.random(); 随机0 - 1之间的小数
Math.random()*10 随机0 - 10之间,公式: Math.random()*x
Math.random()(100-20)+20 随机 5 - 10 之间,公式: Math.random()(y-x)+x
Math.ceil(); 向上取整
Math.floor(); 向下取整
Math.max(); 找最大值
Math.min(); 找最小值
类方法,对象方法,原型方法
function People(name) {
this.name=name;
//对象方法
this.Introduce=function(){
alert("My name is "+this.name);
}
}
//类方法
People.Run=function(){
alert("I can run");
}
//原型方法
People.prototype.IntroduceChinese=function(){
alert("我的名字是"+this.name);
}
//测试
var p1=new People("Jim");
p1.Introduce(); //对象方法需要通过实例化对象去调用
People.Run(); //类方法不需要通过实例化对象去调用
p1.IntroduceChinese(); //原型方法也需要通过实例化对象去调用
1、对象方法包括构造函数中的方法以及构造函数原型上面的方法;
2、类方法,其实这里的类就是一个函数,在js中由于函数也是一个对象,所以可以为函数添加属性以及方法,这种方法在node中用的比较多;
3、原型方法一般用于对象实例共享,比如Person.prototype.sayName=function(){console.log(this.name);};在原型上面添加该方法,就能实现共享。这样就不用每一次初始化一个实例的时候,为其分配相应的内存了。
-----------------Q&&A----------------------
1.typeof
typeof 运算符只能判断:数字,字符串,布尔值,underfine,函数 不能用来判断对象的构造函数
typeof null =“object”
typeof {} [] /abc/=“object”
typeof function(){}===“function”
2.手写new函数
function Person(name, age){
this.name = name;
this.age = age;
}
// 2.1
function create(con,...args){
var obj={};
Object.setPrototypeOf(obj,con.prototype);
const result=con.apply(obj,args);
return result instanceof Object ?result : obj
}
const person1 =create(Person,'Tom',30)
console.log(person1 instanceof Person,person1.name,person1.age)//true "Tom" 20
//2.2
function create2(){
var obj={};
var Con=[].shift.call(arguments)
// 因为slice内部实现是使用的this代表调用对象。那么当[].slice.call() 传入 arguments对象的
// 时候,通过 call函数改变原来 slice方法的this指向, 使其指向arguments,并对arguments进行
// 复制操作,而后返回一个新数组。至此便是完成了arguments类数组转为数组的目的!
// [].shift.call( arguments ) 这便是一个例子。
// shift() 方法删除数组第一项,并返回删除项。
obj.__proto__=Con.prototype
let result=Con.apply(obj,arguments)
return result instanceof Object ? result:obj;
}
// const person1 =create(Person,'Tom',30)
const person1 =create2(Person,'Tom',30)
console.log(person1 instanceof Person,person1.name,person1.age)//true "Tom" 20
2.3
function _new(fn,arg){
const obj=Object.create(fn.prototype);
const ret=fn.apply(obj,arg);
return ret instanceof Object ?ret:obj;}
3.实现instanceof的原理
function myInstance(left,right){
let prototype=right.prototype
left=left.__proto__;
while (true){
if(left===null||left===undefined){
return false;
}
if(prototype===left){
return true
left=left.__proto__
}
}
}
console.log( myInstance(person1,Person))
多态
/多态的基本概念:一个引用类型(变量)在不同情况下的多种状态。
js本身是无态的,天生就支持多态。/