JS面向对象

封装

原始实例对象写法

js中没有class类,所有为了表达一个对象的属性和方法,原始的写法为:

var person1 = {
    name: "wy",
    age: "26",
    run: function () {
        console.log(this.name + "running");
    }
};
person1.run();

这种写法最大的问题是,如果是多个对象就需要多次进行定义声明,对其进行改进

函数式写法

function person1(name, age, run) {
    return {
        name: name,
        age: age,
        run: run
    }
};
var f = function run() {
    console.log("run");
}
var p1 = person1("wy", 26, f);

此时不同的参数可以构造出不同的对象,后来参考java等new出对象的方式,最后形成了下面的构造函数式的写法

构造函数式

function Person(name, age, run) {
    this.name = name;
    this.age = age;
    this.run = run;
};
var f = function run() {
    console.log("run");
}
var p1 = new Person("wy", 26, f);

这种写法相对上面那种,不仅更加符合面向对象的思想和写法,也为继承奠定了基础。
但是思考java中的静态方法,静态属性,既然js是面向对象的,那么这些和类绑定的思想需要如何来进行实现了,这个就涉及到了原型。我们可以先简单理解为原型就是java中的类对象,里面的属性和方法类似java的静态方法,比如person对象的公共方法可以写为

prototype

function Person(name, age, run) {
    this.name = name;
    this.age = age;
    this.run = run;
};

Person.prototype.sleep=function () {
    console.log("sleep")
}
var f = function run() {
    console.log("run");
}
var p1 = new Person("wy", 26, f);
var p2 = new Person("sz", 76, f);
Person.prototype.sleep();
p1.sleep();
p2.sleep();

此时,p1,p2都可以调用原型定义的方法sleep,设置Person.prototype也可以调用,因为此时我们把Person.prototype就当做class类对象,当然可以调用其静态方法sleep。

继承

apply继承

function Person() {
    console.log("run");
};

function Sofer(code) {
    Person.apply(this,arguments);
    this.code = code;
}

var s1=new Sofer("js");
console.log(s1.code);

这种继承方式通过apply将Person函数直接‘copy’过来了,上篇文章JS基础讲过,apply可以改变函数的this指向,更可以理解为copy了一个这样的函数。
但是这样写法有个很大缺陷,定义的原型方法无法继承过来,毕竟只是拷贝了一个函数,并不是真正的面向对象的思想,比如

function Person() {
    console.log("run");
};
Person.prototype.eat = function () {
    console.log("eat");
};

function Sofer(code) {
    Person.apply(this, arguments);
    this.code = code;
}

var s1 = new Sofer("js");
console.log(s1.code);
s1.eat();//Uncaught TypeError: s1.eat is not a function

prototype模式

function Person() {
    console.log("run");
};
Person.prototype.eat = function () {
    console.log("eat");
}

function Sofer(code) {
    this.code = code;
}

Sofer.prototype = new Person();//run
Sofer.prototype.constructor = Sofer

var s1 = new Sofer("js");
console.log(s1.code);//js
s1.eat();//eat

通过子类原型指向父类对象,然后在将子类原型的构造函数指向自己本身,这种方式用的最多。

继承父类的公共属性和方法

如果只需要继承父类的公共属性和方法,即都是原型里面的属性方法,那么可以子类原型指向父类原型

function Person() {
    this.filed1 = "filed1";
}

Person.prototype.eat = function () {
    console.log("eat");
};

function Sofer(code) {
    this.code = code;
}

Sofer.prototype = Person.prototype;
Sofer.prototype.constructor = Person;

var s1 = new Sofer("js");
console.log(s1.code);//js
s1.eat();//eat
console.log(s1.filed1);//undefined

继承父类的公共属性和方法改进

子类原型指向父类原型会增加内存开销,因为此时子类原型的修改都修改父类的原型,所以可以用一个空对象作为中介:

var F = function () {
};

function Person() {
    this.filed1 = "filed1";
}

Person.prototype.eat = function () {
    console.log("eat");
};

function Sofer(code) {
    this.code = code;
}

F.prototype = Person.prototype;

Sofer.prototype = new F();
Sofer.prototype.constructor = Person;

var s1 = new Sofer("js");
console.log(s1.code);//js
s1.eat();//eat
console.log(s1.filed1);//undefined

此时子类的原型对象的修改就不会影响父类的原型了。F的对象内存也会很小。

原型链

概念:

  1. 所有的函数都有prototype属性,指向函数的原型对象。(显式原型)
  2. 所有的对象都有__proto__属性,指向是该对象的构造函数的原型。(隐式原型)
function Person(name){
    this.name = name;
}
var p1 = new Person('louis');

console.log(Person.prototype);//Person原型 {constructor: Person(name),__proto__: Object}
console.log(p1.__proto__ == Person.prototype);//true

Person.prototype的log结果是constructor和__proto__,其中,constrctor指代的就是Person这个函数,因为其就是构造函数(自己的原型属性指向了自己);__proto__是所有对象都有的属性,所以对象Person.prototype也会有该属性,根据定义,其指向的应该是Person.prototype的构造函数的原型对象,那到底是什么呢?p1的__proto__指向构造函数的原型对象,和概念一致。

原型链探究

下面我们来深究下__proto__

console.log(Person.__proto__ === Function.prototype);//true

这行代码说明构造函数构造函数是Function,类似java的Class,Method等,事实上,所有的构造器都继承于Function.prototype

console.log(Math.__proto__ === Object.prototype);  // true
console.log(JSON.__proto__ === Object.prototype);  // true

但是Math,JSON是以对象形式存在的,无需new
那什么是Function.prototype呢

Function.prototype

console.log(typeof Function.prototype) // "function"
实际上, Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象。如下:
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Object.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object

作者:路易斯
链接:https://juejin.im/post/58f9d0290ce46300611ada65

再看

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

再看

console.log(Object.prototype.__proto__ === null);//true,什么都没有,Object就是基类无疑了

所以,普通构造函数的构造函数是Function,
Function的原型的构造函数是Object,
Object的原型的构造函数是null

proto

只要是函数,它的__proto__就指向 Funtion.prototype. 即函数的构造函数都为Function
对于非函数,其指向的是构造函数的原型。
比如

console.log(Object.__proto__   == Function.prototype);//true
console.log(Function.__proto__ == Function.prototype);//true

原型总结

  1. 1.在JS里,万物皆对象。函数是一等公民。
  2. 函数有prototype,它是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。
  3. 对象有属性__proto__,指向对象的构造函数的原型对象。
  4. Function是所有函数的构造函数
  5. Function.prototype的构造函数是Object,Object.prototype的构造函数是null
  6. 原型可以类比java的静态属性,方法,隐式原型构成了原型链,使得js具有面向对象的可能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值