JavaScript学习总结(一)——类和继承

本文详细探讨了JavaScript中的继承概念,包括对象的原型、继承的实现方式,如`Object.create()`和自定义函数。深入讲解了类的组成部分,如实例字段、实例方法、类字段和类方法。此外,还讨论了ECMAScript5和6中对类的修饰及继承的语法差异,包括静态方法、属性特性以及如何使用`super`关键字。
摘要由CSDN通过智能技术生成

一、继承

1、概念
  • f.prototype原型:函数的原型,当用函数f作为构造函数创造实例时,实例instance会继承f.prototype;
  • obj.__ proto__原型对象:一种浏览器支持的非标准方式;obj可以为任何对象,obj2.__ proto__=obj;就直接表示obj2继承自obj。
    ####2、继承的语法
  1. ES5定义了obj2 = Object.create(obj)方法来实现继承关系;调用此函数后obj2继承自obj;
  2. 针对没有Object.create()方法的ES3,我们定义了下面inherit函数;通过这个函数的实现,可以看出继承的关键:
    1. 首先定义一个构造函数f;
    2. f.prototype = p;f的原型为p
    3. 构造f的实例new f();
  3. obj2.__ proto=obj;可以直接实现obj2继承自obj的语法
    A继承了B,则:
  • 如果在A中查询属性x,如果A中不存在,则在A的原型对象B中查找;如果B中也没有,则在B的原型对象中查找,以此类推,直到原型对象链结束。
  • 如果给A中属性x赋值:
    1. 如A中有已有x(不是继承来的),那么就直接改变这个x的值(受writable特性的限制);
    2. 如A中没有x,而其原型对象链中有(且可写),那么A中添加x,并给它赋值;而它会覆盖原型对象链中的x(以后通过A访问x都是这个x);
    3. 如A中没有x,而其原型对象链中有(但不可写),赋值操作不造成任何影响;
    4. 对getter和setter进行操作时,writable特性,是依据getter和setter内部修改的实际属性。
  • 查询操作会受继承的影响,赋值操作只是对对象本身的修改(当原型对象链中有不可修改的属性时,对象中不能添加此属性);
/***********************************************
 * inherit()返回一个继承自原型对象p的属性的新对象
 * 这里使用ECMAScript 5中的Object.create()函数(如果存在的话)
 * 如果不存在Object.create()方法,则退化使用其他方法
 ***********************************************/
function inherit(p) {
   

    if (Object.create) return Object.create(p); //使用Object.create方法
    /* 检测p的类型是否符合要求 */
    var t = typeof p;
    if (t !== 'object' && t !== 'function') throw TypeError();
    /* 下面的原型创建方法,类似于Object.create(p);  */
    function f() {
   };
    f.prototype = p;
    return new f();
}
/***
 * 此例中定义了对象a,具有可修改的属性x和存取器y
 * 此例中定义了对象b,具有不可修改的属性x和存取器y
 * c继承自a,d继承自b;调用c和d的setter后,观察对象getter返回值的变化
 * */
//a有属性x(可读写),存取器属性y(可读写)
var a = {
    x: 1 };
Object.defineProperty(a, 'y', {
   
    get: function() {
   
        return this.x + 1;
    },
    set: function(x) {
   
        this.x = x - 1;
    },
    configurable: true,
});
//b有属性x(不可写),存取器属性y(可读写)
var b = {
   };
Object.defineProperty(b, 'x', {
    value: 1, configurable: true });
Object.defineProperty(b, 'y', {
   
    get: function() {
   
        return this.x + 1;
    },
    set: function(x) {
   
        this.x = x - 1;
    },
    configurable: true,
})

var c = Object.create(a);
c.y = 3;
var d = Object.create(b);
d.y = 3;
//由于a中x可读写,所以给c添加一个熟悉x;而b中x不可写,调用setter没有任何效果
console.log(a.y); //2
console.log(c.y); //3
console.log(b.y); //2
console.log(d.y); //2


二、类

1、JavaScript中的类型

JavaScript中只有两大类型:原始值和对象;类型通过typeof运算符来得到

1.1、原始值

原始值的特点是不可修改(在那个特定的内存区域保存的就是这个原始值的值);我们可以让变量指向另一个原始值,但无法改变原始值内部的内容。

  1. undefined
  2. null
  3. Number
  4. Boolean
  5. String
  6. Symbol: 一种特殊的string序列(独一无二)
    #####1.2、对象
  7. 对象本身就是一个引用,它指向一个集合的地址,集合内是对象的属性;我们修改集合内的对象属性,不改变对象的值,只有当我们将对象指向另一个集合时,才会改变对象的值。
  8. JavaScript作为一个弱类型的语言,它的对象类型非常灵活。面对如此庞杂的对象,我们要识别它,就引入了类的概念。
    ####2、类的概念
    #####2.1、类的基本组成
    类,顾名思义,就是对对象进行分类和抽象,它主要有下面几部分的内容:
  9. 实例字段:它们是基于实例的属性或变量,用以保存对立对象的状态;在js中,就是构造函数中this的属性。
  10. 实例方法:他们是类的所有实例所共享的方法,由每个独立的实例调用;在js中,通过原型来实现,就是constructor.prototype的方法;
  11. 类字段: 这些属性或变量属于类的,而不是属于类的某个实例的;在js中,就是constructor.properties;
  12. 类方法: 这些方法属于类的,而不是属于类的某个实例的;在js中,就是constructor.methods;

上面的2、3、4项,都是为了实现某种类型的共享。区别主要有:实例方法,可以在函数内部直接使用this来访问实例的属性和方法;而类的方法和字段,类似于全局函数和变量,作为构造函数的属性,只是在标识符上用类名做了一个区分。

2.2、类的各部分关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M1IQ1eJb-1593672875475)(./asset/proto.jpg)]

以Persion类为例,令 proto = Persion.prototype;类需要具备以下条件:

  1. proto.constructor = Persion;
  2. Persion.prototype = proto;
  3. instance = Object.create(proto);在构造函数里,我们通过new来实现这个关系,在工厂函数中,我们可以通过手动实现。

上面的第三条是类的根本,要检验 instance instanceof Persion 实际上就是 instance的继承链上是否有Persion.prototype,无论是不是直接继承。

/**factory method
 * 一个简单的范围类
 */
function range(from, to) {
   
    var r = Object.create(range.prototype);//上面的条件3
    r.from = from;
    r.to = to;
    return r;
}
range.prototype = {
   //上面的条件2
    constructor: range, //上面的条件1
    includes: function(x) {
   
        return this.from <= x && x <= this.to;
    },
    foreach: function(f) {
   
        for (var x = Math.ceil(this.from); x <= this.to; ++x) f(x);
    },
    toString: function() {
   
        return '(' + this.from + '...' + this.to + ')';
    }
};
var r = range(1, 3);
console.log(r instanceof range);//true;满足了上面三个条件,所以r是range的实例

2.3、类的各部分关系的再深入

javaScript中,类主要由constructor、constructor.prototype两部分组成。constructor有两部分:构造函数本身,创建instance数据;constructor的属性是类的静态方法和数据

/**一个定义简单类的函数 */
/**extend 
 * 拓展对象o中的属性(只处理可枚举属性,包括继承来的属性)
 * 如果o与p中有同名属性,则覆盖o中属性
 * @param {Object} o 
 * @param {Object} p
 * @return {Object} 
 */
function extend(o, p) {
   
    for (var prop in p) {
   
        o[prop] = p[prop]; //浅复制
    }
    return o;
}

/**defineClass
 * 通过构造函数constructor、实例方法method、类数据和类方法static来构造类
 * @param {Function} constructor 
 * @param {Object} method 
 * @param {Object} static 
 * @return {Function}
 */
function defineClass(constructor, method, static) {
   
    if (method) extend(constructor.prototype, method);
    if (static) extend(constructor, static);
    return constructor;
}

//constructor
function Complex(real, imaginary) {
   
    if (isNaN(real) || isNaN(imaginary)) throw TypeError("real and imaginary must be number!");
    this.r = real;
    this.i = imaginary;
}
//methods
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值