什么是面向对象,原型,原型链继承

Javascript是一种基于面向对象的语言,你所遇到的东西几乎几乎都是对象。面向对象重要的两个概念:类和对象。这里要分2种情况,在ES6之前,和ES6语法,这里先说ES6之前,ES6之前是没有类的概念的,于是就通过构造函数去模拟类,你可以理解为他们只是人类为了移民火星yy出来的一个概念,他并不是实际存在的只是一个概念,然后就靠着这个概念去一点点的摸索创造,最后终于给造出来了一个飞向火星的太空船,那么这个太空船就是类的实例化的体现。这就是对象。类又是什么?属性和行为。太空船是什么颜色的由什么组成,这是他的属性,他能飞这就是他的行为,那么ES6之前通过这样通过函数来模拟类的存在。那么怎么创建一个构造函数,有以下三种方式:
一、构造函数的创建
1.js内置的构造函数(String、Object、Array、New、Date、number、function)等,再为他创建属性和方法

var obj = new Object()
obj.name = "小明"
obj.age = "20"
obj.sayName = function(){
    alert(obj.name)  //小明
}

2.通过字面量创建,还是上面这个例子

var obj = {
name = “小明”,
age = 20,
sayName : function(){
alert(this.name)
}
};
通过这两个例子你会发现,有很多是重复的代码,这并不是一个好的选择,讲完了这2个方式咱们穿插一下,为了解决这个问题,人们开始使用工厂模式—进口—>生产—>—>出口,既然都是一样的那就批量来解决。在出口用变量来接收,在这个函数里输入参数

function createObj(name,age) {
         var a = new Object()
             a.name = name
             a.age = age
             a.sayName = function(){
             alert(this.name)
     }
     return a
}
var a1 = createObj("小明",18)
var a2 = createObj("小红",20)
console.log(a1)  

通过createObj( )能够根据接收的参数来构建一个包含所有必要信息的Obj对象,可以无数次的调用,每次返回包含2个属性一个方法的对象。不用重复的去写相同的代码,但是,工厂模式虽然解决了这种重复性的问题,但却没有解决怎样去知道这样一个对象的类型,怎么去识别对象。我们来看一下第三种构造函数模式
3.自定义构造函数

function createObj(name,age) {
        this.name = name;
        this.age = age;
        this.sayName = function() {
        alert(this.name)
     }
}
var obj1 = new Person('小明',20)
var obj2 = new Person('小红',18)

细心的朋友可以找一下,这种方式和上一种的不同之处(1)直接将属性和方法赋给了this对象。(2)没有return语句。(3)创建实例对象使用了new操作符
在这个例子中obj1 和obj2分别保存着createObj的两个不同的实例,这两个对象都有一个constructor(构造函数)属性,该属性指向createObj
可以这样测试
alert(obj1.constructor == createObj); //true
constructor最初是用来标示对象类型的,但是提到检测对象类型可以使用instanceof操作符更可靠一些

alert(obj1 instanceof createObj);//true

构造函数和其他函数的唯一区别:调用的方式不同,任何函数通过new操作符来调用,那他就可以作为构造函数,同理,如果不通过new操作符来调用他就是普通函数。
任何事情再好也总是有缺憾,这又上升到哲学层面上了,所以不懂哲学大的IT工程师不是好艺术家。。。那么构造函数的问题是什么呢?就是每个方法都要在每个实例上重新创建一遍,因此每定义一个函数也就是实例化一个对象,这样一来我们就要定义很多个函数,这样自定义函数也就没有任何的封装性了,好在我们可以使用原型模式来解决。
二、原型模式
首先我们要知道,什么是原型?需不需要我们自己去创建?通过一个代码你就知道。原型的两个关键词: 1.prototype 2. proto

    function Person(name,age) {
            this.name = name;
            this.eat = function() {
                alert('吃饭')
            }
        }
        Person.prototype.age = 20
        var p1 = new Person("小明",20)
        console.log(p1)

什么是面向对象,原型,原型链继承_第1张图片
在这个基础上我们再给他添加一个方法

    function Person(name,age) {
            this.name = name;
            this.eat = function() {
                alert('吃饭')
            }
        }
        Person.prototype.age = 20
        Person.prototype.run = function(){
            console.log('我能跑')
        }
        var p1 = new Person("小明",20)
        p1.run()
        console.log(p1)

什么是面向对象,原型,原型链继承_第2张图片
你就可以看到通过prototype添加的原型对象是保存在proto里的。他d的私有属性是name,age和run是保存在原型里的。由此原型对象最重要的一点就是,通过调研构造函数而创建的那个对象实例的原型对象,使用原型对象的好处他是共享的!那么如果代码非常多的话,我们又怎么知道哪些是共享的哪些是私有的

console.log(Person.prototype.isPrototypeOf(p1)) //判断对象是否指向构造函数的原型对象,返回true/false
console.log(p1.hasOwnProperty('name')) // 判断对象的属性是在原型中还是实例中

既然说到在原型里是可以共享的,那该怎么得到他的属性和方法呢?
在控制台里你可以看到,每一个构造函数都有一个原型对象。proto
假如一个原型又是另一个类型的实例,如此层层递进,就构成了实例与原型的链条,这就是所谓的原型链的基本概念,在业务中,我们父类和子类的属性或者方法是相同的我们就可以继承原型里的属性和方法。原型是可以共享的!前提是要有相同的业务场景和逻辑,这就是继承,许多的OO语言都支持两种继承方式,接口继承和实现继承,ECMAscript只支持实现继承,而实现继承主要依靠原型链。原型链的继承首选会通过自身查找如果自己找不到会一直像上级查找一直找到object。我们还是通过代码来了解继承!
原型继承这里介绍2种方式
三、继承
1.原型继承、
首选还要知道一个概念,被继承的构造函数叫做(父类、基类)

    function Father(name,money) {
            this.name = name;
            this.money = money;
        }
        Father.prototype.height = 180
        Father.prototype.eat = function() {
            console.log('我会吃饭')
        }
        var f = new Father('王','1000万')

        function Son(name,money) {
        }
        Son.prototype = new Father()
        var s = new Son()
        s.eat()
        s.name = "jack"
        console.log(s)

这里父亲的私有属性并没有赋值,原型里的height =180 eat()
什么是面向对象,原型,原型链继承_第3张图片
此时Son已经继承了Father的height和方法eat
原型继承的缺憾:没有自己的私有属性,继承来的都放在里原型里
只有通过自己手动去添加。s.name = jack,如果添加到原型里是s.proto.name = “jack”
2.对象冒充(call、apply)
要知道call,和apply的用法
什么是面向对象,原型,原型链继承_第4张图片
什么是面向对象,原型,原型链继承_第5张图片
对象冒充的缺点:1.不能继承父类原型的属性和方法;2.继承之后会形成私有属性和方法。
3.组合继承:
既可以继承之后形成私有属性,也可以继承父类原型中的属性行为
什么是面向对象,原型,原型链继承_第6张图片

以上说的都是ES6之前的语法,大家都知道ES6已经有了类的概念,也非常的简单。ES6的class没有变量提升, 类必须先声明再使用,否则会出现异常。ES6的类是class,继承是extends,敲完下面的代码你就会明白


class Father {
//constructor构造函数,初始化一些属性值
constructor(name,age){
this.name = name;
this.age = age;
}
run() {
alert('跑起来')
}
//static 关键字,用来定义一个类的静态方法,通过类名调用 Father.eat()
static eat() {
alert('吃饭')
}
speak() {
alert(this.name+'Father.speak')
}
}
let F = new Father('大毛',60)
console.log(F)
// F.run()
// F.eat()是错的
// Father.eat()

什么是面向对象,原型,原型链继承_第7张图片

你可能感兴趣的:(JS,javascript,面向对象,对象,继承)