原型和原型链

定义

  • proto 对象中有个属性proto,被称为隐式原型,这个隐式原型指向构造改对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。按照标准,proto 是不对外公开的,但是 chrome 和firefox的引擎却将他暴露了出来成为了一个公有属性,我们可以对其进行访问和赋值。但 ie 浏览器是不能访问这个属性的,所以不推荐大家直接操作这个属性,以免造成浏览器兼容问题。(现在__proto__属性被标准化了)
proto1.jpg
  • -proto- chrom 浏览器实现的对外可访问的变量 ,firefox浏览器对外实现的可访问变量<prototype>

    firefox:

proto0.jpg

chrom:

proto01.jpg
  • prototype 函数对象 独有的属性 重要的事情说三遍!三遍三遍

简单来说,在 javascript 中每个对象都会有一个 proto 属性,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 proto 里找这个属性,这个 proto 又会有自己的 proto,于是就这样一直找下去,也就是我们平时所说的原型链的概念,从技术上讲,这种机制被称为动态调度(dynamic dispatch)或者委托

原型对象也是普通的对象,并且也有可能有自己的原型,默认对象实际上永远不会是空的 - 它总会从Object.prototype继承一些东西。如果要创建一个无原型的词典,我们必须显式将其原型设置为null

// 不继承任何东西。
let dict = Object.create(null);
console.log(dict.toString); // undefined

如果一个原型对象的原型不为null的话,我们就称之为原型链(prototype chain)。

原型链(Prototype chain)

定义:原型链是用于实现继承和共享属性的有限对象链。

想象一个这种情况,2个对象,大部分内容都一样,只有一小部分不一样,很明显,在一个好的设计模式中,我们会需要重用那部分相同的,而不是在每个对象中重复定义那些相同的方法或者属性。在基于类[class-based]的系统中,这些重用部分被称为类的继承 – 相同的部分放入class A,然后class B和class C从A继承,并且可以声明拥有各自的独特的东西。

ECMAScript没有类的概念。但是,重用[reuse]这个理念没什么不同(某些方面,甚至比class-更加灵活),可以由prototype chain原型链来实现。这种继承被称为delegation based inheritance-基于继承的委托,或者更通俗一些,叫做原型继承。

类似于类”A”,”B”,”C”,在ECMAScript中尼创建对象类”a”,”b”,”c”,相应地, 对象“a” 拥有对象“b”和”c”的共同部分。同时对象“b”和”c”只包含它们自己的附加属性或方法。

var a = {
  x: 10,
  calculate: function (z) {
    return this.x + this.y + z
  }
};
 
var b = {
  y: 20,
  __proto__: a
};
 
var c = {
  y: 30,
  __proto__: a
};
 
// 调用继承过来的方法
b.calculate(30); // 60
c.calculate(40); // 80

原理:

如果在对象b中找不到calculate方法(也就是对象b中没有这个calculate属性), 那么就会沿着原型链开始找。如果这个calculate方法在b的prototype中没有找到,那么就会沿着原型链找到a的prototype,一直遍历完整个原型链。记住,一旦找到,就返回第一个找到的属性或者方法。因此,第一个找到的属性成为继承属性。如果遍历完整个原型链,仍然没有找到,那么就会返回undefined。

刘伟硕代码:

//demo1
let cur_time = new Date().Format("yyyy-MM-dd hh:mm:ss");

Date.prototype.Format = function (fmt) {
    var o = {
        "M+": this.getMonth() + 1, //月份
        "d+": this.getDate(), //日
        "h+": this.getHours(), //小时
        "m+": this.getMinutes(), //分
        "s+": this.getSeconds(), //秒
        "q+": Math.floor((this.getMonth() + 3) / 3), //季度
        "S": this.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (let k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}

注:

​ 1.1, ...,9 属性是静态的, 他不是独立的的正则表达式属性. 所以, 我们总是像这样子使用他们RegExp.$1, ..., RegExp.$9.

属性的值是只读的而且只有在正确匹配的情况下才会改变.

括号匹配项是无限的, 但是RegExp对象能捕获的只有九个. 你可以通过返回一个数组索引来取得所有的括号匹配项.

这些属性可以在String.replace 方法中替换字符串. 在这种情况下, 不用在前面加上RegExp。下面的例子将详细说明. 当正则表达式中不包含括号, 脚本中的 $n's 就是字面上的意思 (当n是正整数).

​ 2.replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

stringObject.replace(regexp/substr,replacement)

​ 3.substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。

stringObject.substr(start,length)

JS 继承

1.for in 遍历整个原型链进行继承

//demo 乱序 插进来的
function ClassA(){
    this.name ="bianbumei";
    this.age="88";
    this.sayAge=function (){
        alert(`my age is ${this.age}`)
    }
}
function ClassB(){
    let instanceA=new ClassA();
    let me =this;
    for (let oneProName in instanceA){
        console.log(`${oneProName}: ${instanceA[oneProName]}`)
        me[oneProName]=instanceA[oneProName]
    }
    console.log(me)
}
let instanceB=new ClassB();
console.log("instanceB::",instanceB)

2.使用原型链进行继承

//demo2
function ClassA() {
}

ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB() {
}

ClassB.prototype = new ClassA();

let instanceClassB=new ClassB()
console.log(instanceClassB)
//
console.log(`ClassB :${instanceClassB}`)
//demo3
function ClassA() {
}

ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB() {
}

ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
    //this 指向方法的调用者
    alert(this.name);
};
let instanceClassB=new ClassB()
instanceClassB.name="xiaobian";
console.log(instanceClassB)
instanceClassB.sayName();
//demo4
function ClassA() {

}
    ClassA.prototype.color = "blue";
    ClassA.prototype.sayColor = function () {
        alert(this.color);
    };
//如果将赋值语句放到ClassB的构造函数中会发生什么
function ClassB() {
ClassB.prototype = new ClassA();
}

let instanceClassB1=new ClassB()
console.log(instanceClassB1)//没有对应的方法
let instanceClassB2=new ClassB()
console.log(instanceClassB2)//有了对应的方法
//这是为什么呢?
console.log(`ClassB :${instanceClassB2}`)

继承的演变过程

extends.png

1.Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

//demos
let baba={
    name:"mayun",
    money:100*10000,
    face:"caixukun",
    car:"aotuo",
    work_count_money:function(){
        console.log(`${this.name}的钱,我数了!很好玩!总共有${this.money}`);
    }
}
let me=Object.create(baba);
me.name="liangsijie";
me.face="shiwaxinge";
me.work_count_money();
console.log(me)

Object.create()的底层实现相当于如下代码:

function create(o){
  function F(){}
  F.prototype = o;
  return new F();
}

2.原型链继承

  • 优点:父类方法可以复用
    缺点:
    • 父类的引用属性会被所有子类实例共享
    • 子类构建实例时不能向父类传递参数

3. 构造函数继承

核心:将父类构造函数的内容复制给了子类的构造函数。这是所有继承中唯一一个不涉及到prototype的继承。

优点:和原型链继承完全反过来。

  • 父类的引用属性不会被共享
  • 子类构建实例时可以向父类传递参数

缺点:父类的方法不能复用,子类实例的方法每次都是单独创建的。

new到底做了什么?

作用域中将。。。

构造函数

定义:

构造函数是一个用于创建实例,并自动设置实例的原型的函数

例子1:

function Letter(number) {
  this.number = number;
}
Letter.prototype.getNumber = function() {
  return this.number;
};
let a = new Letter(1);
let b = new Letter(2);
// ...
let z = new Letter(26);
console.log(
  a.getNumber(), // 1
  b.getNumber(), // 2
  z.getNumber(), // 26
);
proto2.jpg

例子2:


function Persion(name,job){
    this.name=name;
    this.job=job;
    
}

Persion.myName=function(){
    return "lsj";
}
Persion.prototype.age=26;
// Persion.age=27;
Persion.prototype.sayName=function(){
    return "mahuateng";
    
};
console.log(new Persion("xiaoBian","manong").sayName()); //26
console.log(new Persion("xiaoBian","manong").age); //26
console.log(new Persion("xiaoBian","manong").myName()); //Uncaught TypeError

proto3.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342