关于JavaScript中面向对象

什么是面向对象?

在java、actionscript等语言中都有对象(类的概念),我的理解是对象就是属性和方法的集合,在编程语言中有一句话叫一切皆对象,Object对象是所有对象的基类,都是继承Object;

在js中对象的创建:

一、用new关键字创建对象:
var obj=new Object()
new 关键字是语法糖,他创建了一个实例对象,继承了Object的属性和方法,以下是new 经历哪些步骤和代码模拟

<script type="text/javascript">
    function Persion(){
        /**
        *func:构造函数
        */
        this.dec="这是描述";
    }
    Persion.prototype.sayHell=function(){
        console.log("您好");
    }

    function desaginNew(tt){
        /*
        *func:模拟new关键字;
        */
        //第一步:创建新的对象;
        var obj={};
        var creatObj=[].shift.call(arguments);

        //第二步:改变this指向,把新的对象的_proto_指向Persion对象的prototype原型对象;
        obj._proto_=creatObj.prototype;

        //第三步:执行构造函数的代码为新对象添加属性和方法;
        Persion.call(obj);

        //第四步:返回对象;
        return obj;
    }
    var newPersion=desaginNew(Persion);
</script>

二、用字面量创建对象:
var obj={name:"",age:"",say:function(){}}
三、构造函数创建对象:
构造函数:在java语言中类的定义中都有构造函数,在js中也有构造函数的概念,Object对象都有constructor构造函数方法
js中Object对象都有以下属性和方法:
1、constructor:构造函数属性,它指向了Object()函数
2、hasOwnProperty:判断是否是对象自身属性和方法
3、isPrototypeOf:判断是否是某个对象的子对象即继承某对象

function Persion(){
        /**
        *func:构造函数
        */
        this.name="名字";
        this.say=function(){
            console.log("您好");
        }
    }
    //自身属性要实例化后才能访问属性和方法;
    console.log(new Persion().name);//"名字"
    console.log(new Persion().say());//"您好;

四、es6中新增class关键字创建类

 // ES6 的类
    class Person {
      constructor (name) {
        // 构造函数体
        this.name = name
      }

      // 这里就是原型
      fn1 () { console.log('我是 Person 原型上的方法 fn1') }

      fn2 () { console.log('我是 Person 原型上的方法 fn2') }
    }

    let p1 = new Person('Jack')
    console.log(p1)

原型

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

function Box() {} //声明一个构造函数
Box.prototype.name = 'Lee'; //在原型里添加属性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型里添加方法
return this.name + this.age + '运行中...';
};

它解决了消耗内存问题。当然它也可以解决this作用域等问题。
我们经常把属性(一些在实例化对象时属性值改变的),定义在构造函数内;把公用的方法添加在原型上面,也就是混合方式构造对象(构造方法+原型方式):

var person = function(name){
   this.name = name
  };
  person.prototype.getName = function(){
     return this.name; 
  }
  var zjh = new person(‘zhangjiahao’);
  zjh.getName(); //zhangjiahao

下面详细介绍原型:

1.原型对象
  每个javascript对象都有一个原型对象,这个对象在不同的解释器下的实现不同。比如在firefox下,每个对象都有一个隐藏的__proto__属性,这个属性就是“原型对象”的引用。

2.原型链
  由于原型对象本身也是对象,根据上边的定义,它也有自己的原型,而它自己的原型对象又可以有自己的原型,这样就组成了一条链,这个就是原型链,JavaScritp引擎在访问对象的属性时,如果在对象本身中没有找到,则会去原型链中查找,如果找到,直接返回值,如果整个链都遍历且没有找到属性,则返回undefined.原型链一般实现为一个链表,这样就可以按照一定的顺序来查找。

1)__proto__和prototype
JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子为例:

console.log(zjh.__proto__ === person.prototype) //true

同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype

console.log(person.prototype.__proto__ === Object.prototype) //true

继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null

console.log(Object.prototype.__proto__) //null

2)constructor
原型对象prototype中都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用

person.prototype.constructor === person //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true

3)为加深对理解,我们再举一个例子:

function Task(id){  
    this.id = id 
}  
    
Task.prototype.status = "STOPPED"  
Task.prototype.execute = function(args){  
    return "execute task_"+this.id+"["+this.status+"]:"+args  
}  
    
var task1 = new Task(1) 
var task2 = new Task(2)  
    
task1.status = "ACTIVE"  
task2.status = "STARTING"  
    
print(task1.execute("task1"))  
print(task2.execute("task2"))

结果:

execute task_1[ACTIVE]:task1
execute task_2[STARTING]:task2

总结:
实例对象的__proto__指向,其构造函数的原型;构造函数原型的constructor指向对应的构造函数。构造函数的prototype获得构造函数的原型。有时某种原因constructor指向有问题,可以通过

继承

继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。在JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)
1.call+遍历
属性使用对象冒充(call)(实质上是改变了this指针的指向)继承基类,方法用遍历基类原型。

function A()
{
    this.abc=12;
}

A.prototype.show=function ()
{
    alert(this.abc);
};

//继承A
function B()
{
    //继承属性;this->new B()
    A.call(this);   //有参数可以传参数A.call(this,name,age)
}

//继承方法;B.prototype=A.prototype;
for(var i in A.prototype)
{
    B.prototype[i]=A.prototype[i];
}
//添加自己的方法
B.prototype.fn=function ()
{
    alert('abc');
};

var objB=new B();
var objA=new A();objB.show();

可以实现多继承。

2.寄生组合继承
主要是Desk.prototype = new Box(); Desk 继承了Box,通过原型,形成链条。主要通过临时中转函数和寄生函数实现。

//临时中转函数
function obj(o) { //o表示将要传递进入的一个对象
function F() {}   //F构造是一个临时新建的对象,用来存储传递过来的对象
F.prototype = o;  //将o对象实例赋值给F构造的原型对象
return new F();   //最后返回这个得到传递过来对象的对象实例
}
//寄生函数
function create(box, desk) {
var f = obj(box.prototype);
f.constructor = desk;  //调整原型构造指针
desk.prototype = f;
}
function Box(name) {
this.name = name;
this.arr = ['apple','pear','orange'];
}
Box.prototype.run = function () {
return this.name;
};
function Desk(name, age) {
Box.call(this, name);
this.age = age;
}
//通过寄生组合继承实现继承
create(Box, Desk); //这句话用来替代Desk.prototype = new Box();
var desk = new Desk('Lee',100);
desk.arr.push('peach');
alert(desk.arr);
alert(desk.run());

PS:如果想获取更多的知识视频,加QQ好友10398975免费送给大家

猜你喜欢

转载自blog.csdn.net/m0_46486334/article/details/106680525