JS面向对象编程

创建对象

JavaScript对每个创建的对象都会设置一个原型(__proto__),指向它的原型对象。

当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

  • eg: 创建一个Array对象: var arr = [1, 2, 3];
    其原型链是:
    arr ----> Array.prototype ----> Object.prototype ----> null
    Array.prototype定义了indexOf()shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
  • eg: 创建一个函数时:
    function foo() { return 0; }函数也是一个对象,它的原型链是:
    foo ----> Function.prototype ----> Object.prototype ----> null
    由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

JS原型和原型链简介

1. 构造函数

构造函数是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)属性

  • 传统创建对象实例的方法
var person={
      name:'张女士',
      age:'80',
      gender:'女'
  };
console.log(person)

注:这个方法如果用于创建大量相同属性和方法的对象时,会产生大量重复代码

  • 构造函数的方法
function Student(name) {
    this.name = name;
    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student('小明');

注意:如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;
xiaoming的原型链是: xiaoming ----> Student.prototype ----> Object.prototype ---->null

  • 每个( 原型对象 )都有一个constructor属性,指向prototype属性所在的函数
    eg:
    xiaoming.constructor == Student; //constructor指向构造函数
    xiaoming.constructor == Student.prototype.constructor == Student

2.原型模式

每个函数都有原型属性(__proto__),这个属性是一个指针,指向一个对象,这个对象(prototype)的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。而每个实例内部都有一个指向原型对象的指针

如果我们通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。
要让创建的对象共享一个hello函数,根据对象的属性查找原则,我们只要把hello函数移动到xiaoming、xiaohong这些对象共同的原型上就可以了,也就是Student.prototype

function Student(name) {
    this.name = name;
}
Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};
  • 还可以编写一个createStudent()函数,在内部封装所有的new操作
function Student(props){
     this.name = props.name || '匿名'; //默认值为匿名
     this.grade = props.grade || 1;
}
Student.prototype.hello = function(){
    alert('Hello, '+this.name+'!');
}
function creatStudent(props){
    return new Student(props || {});
}

这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:
var xiaoming = createStudent({name: '小明'});
xiaoming.grade; // 1
如果创建的对象有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序。如果恰好从JSON拿到了一个对象,就可以直接创建出xiaoming。

3. 原型链

每个构造函数都有原型对象,每个构造函数实例都包含一个指向原型对象的内部指针(__proto__)(即:实例.__proto__ === 构造函数.prototype),如果我们让第一个构造函数的原型对象等于第二个构造函数的实例,结果第一个构造函数的原型对象将包含一个指向第二个原型对象的指针,再然第三个原型对象等于第一个构造函数的实例,这样第三个原型对象也将包含指向第一个原型对象的指针,以此类推,就够成了实例于原型的链条,这就是原型链的基本概念

function One(){
}
function Two(){
}
function Three(){
}
Two.prototype=new One();
            
Three.prototype=new Two();
            
var three=new Three();

console.log(three);
console.log(three.__proto__===Three.prototype) //true
console.log(three.__proto__.__proto__===Two.prototype) //true
console.log(three.__proto__.__proto__.__proto__===One.prototype)  //true
console.log(three.__proto__.__proto__.__proto__.__proto__===Object.prototype)  //true

在对象实例中,访问对象原型的方法
1)使用proto属性:此属性是浏览器支持的一个属性,并不是ECMAScript里的属性
2)Object.getPrototypeOf
3)使用constructor.prototype的方法:对于不支持proto的浏览器,可以使用constructor,访问到对象的构造函数,在用prototype访问到原型

  • 自我理解
function One(){
}
function Two(){
}
function Three(){}

let one = new One();
one.__proto__ == One.prototype;
one.__proto__.__proto__ == One.prototype.__proto__ == Two.prototype;
one.__proto__.__proto__.__proto__ == Two.prototype.__proto__ == Three.prototype;

如果第一个构造函数的原型对象等于第二个构造函数的实例 ==> 第一个构造函数的原型对象包含指向第二个构造函数原型对象的指针
One.prototype = new Two(); One.prototype.__proto__ == Two.prototype;
如果第二个构造函数的原型对象等于第三个构造函数的实例 ==> 第二个构造函数的原型对象包含指向第三个构造函数运行对象的指针
Two.prototype = new Three(); Two.prototype.__proto__ == Three.prototype
第一个构造函数的实例的原型指向第一个构造函数的原型对象,第一个构造函数的原型对象的原型指向第二个构造函数的原型对象,第二个构造函数的原型对象的原型指向第三个构造函数的原型对象

  • 总结
    __proto__为函数(实际上__proto__是所有对象(包括函数)都有的,原型链是靠它形成的)的原型属性
    prototype 为原型对象(准确的来说只有构造函数才有的,它跟原型链没有关系)
    每个object的原型属性(__proto__)指向指向他的原型(prototype

原型继承

  • 把继承这个动作用一个inherits()函数封装起来
function inherits(Child, Parent) {
    var F = function () {}; //注意,函数F仅用于桥接,我们仅创建了一个new F()实例,而且,没有改变原有的Student定义的原型链。
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容

  • 学过C++等语言的话,你一定明白面向对象的两个基本概念: 类:类是对象的类型模板,例如,定义Student类来表示...
    Beatrice7阅读 756评论 0 0
  • 原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢在网上找点原型链和闭包的文章看,效果极好。 不...
    晨光2016阅读 1,293评论 8 13
  • 大家好,我是IT修真院深圳分院第01期学员,一枚正直纯洁善良的web程序员。 今天给大家分享一下,修真院官网JS(...
    老菜菜阅读 1,277评论 0 2
  • 面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。ECMAScr...
    DHFE阅读 971评论 0 4
  • 栈区(stack)栈区由编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一...
    ChancePro阅读 138评论 0 0