资讯专栏INFORMATION COLUMN

Javascript函数执行、new机制以及继承

sshe / 1132人阅读

摘要:函数执行一个函数,被执行有三种途径或机制以及继承中定义了一种对象,称之为对象,其内部实现包括以下说明该对象可以被执行,具有属性说明该对象可以接受操作,具有构造器属性指向对象的原型链。

JS函数执行

一个JavaScript函数fn,被执行有三种途径:

fn()

new fn()

fn.call()或fn.apply()

new机制以及继承

JavaScript中定义了一种对象,称之为ECMAScript对象,其内部实现包括以下:

__call__: 说明该对象可以被执行,具有function属性

__construct__: 说明该对象可以接受new操作,具有构造器属性

__prototype__: 指向对象的原型链。对于定义的函数,会指向Function.prototype

注意:__prototype__是原型链,对所有对象都有的。prototype是原型,是函数才有的,就是一个普通的对象而已,目的是为了接受new后给生成的对象提供原型链的。

执行fn就是调用__call__

执行new fn()会进行以下简化过程:

新建一个对象,记作o

把o.__prototype__指向fn.prototype(如果fn.prototype不是一个Object,则指向Object.prototype)

执行fn,并用o作为this(即内部实现的fn.call(this))。如果fn返回是一个object,则返回object, 否则把o返回

fn.call(obj)或者fn.apply(obj)就是将obj作为this,执行fn。本质是调用__call__,只是传入了obj作为this.

    //定义一个函数,正常函数会具有__call__, __construct__
    //同时Parent.__proto__指向Function.prototype
    function Parent() {
        this.sayAge = function() {
            console.log("age is: " + this.age);
        }
    }
    //原型上添加一个方法
    Parent.prototype.sayParent = function(){
        console.log("this is Parent Method");
    }
    
    //定义另一个函数
    function Child(firstname){
    
        //这里就是调用Parent的__call__, 并且传入this
        //而这里的this,是Child接受new时候生成的对象
        //因此,这一步会给生成的Child生成的实例添加一个sayAge属性
        Parent.call(this);
        
        this.fname = firstname;
        this.age = 40;
        this.saySomething = function() {
            console.log(this.fname);
            this.sayAge();
        }
    }
    
    //这一步就是new的调用,按原理分步来看
    //1. 新建了个对象,记作o
    //2. o.__prototype__ = Parent.prototype, 因此o.sayParent会访问到o.__prototype__.sayParent(原型链查找机制)
    //3. Parent.call(o), 因此o也会有个sayAge属性(o.sayAge)
    //4. Child.prototype = o, 因此 Child.prototype 通过o.__prototype__ 这个原型链具有了o.sayParent属性,同时通过o.sayAge 具有了sayAge属性(也就是说Child.prototype上具有sayAge属性,但没有sayParent属性,但是通过原型链,也可以访问到sayParent属性)
    Child.prototype = new Parent();
    
    //这也是一步new调用
    //1. 新建对象,记作s
    //2. s.__prototype__ = Child.prototype, 此时s会具有sayAge属性以及sayParent这个原型链上的属性
    //3. Child.call(s), 执行后, 增加了fname, age, saySomething属性, 同时由于跑了Parent.call(s), s还具有sayAge属性, 这个属性是s身上的, 上面那个sayAge是Child.prototype上的, 即s.__prototype__上的。
    //4. child = s
    var child = new Child("张")
    
    //child本身属性就有,执行
    child.saySomething();
    
    //child本身属性没有, 去原型链上看, child.__prototype__ = s.__prototype__ = Child.prototype = o, 这里没找到sayParent, 继续往上找, o.__prototype__ = Parent.prototype, 这里找到了, 执行(第二层原型链找到)
    child.sayParent();

原理来看写得有些繁琐,本身其实是比较简单的东西。
重点是new的过程,原型prototype和原型链__prototype__
也正是new的原理,导致了原型链的继承,本质是生成的对象的__prototype__指向了函数的原型prototype

更复杂的调用继承之类的,都可以通过这个原理来理解。说白了,原型链继承就是复用了prototype而已。

本例来看,Child中的Parent.call(this)看似没有必要,但本质上是有区别的。如果去掉这一句,则Child的实例本身将没有sayAge属性,而Child.prototype具有sayAge属性,因此实例的__prototype__具有sayAge属性,因此还可以执行。

但目的是为了继承,因此属性是需要对象上本身持有,而非是通过原型链上来访问,所以加上这一句是原理上的严谨要求。可以通过下面的例子来检验:

    function Parent() {
        this.sayAge = function() {
            console.log("age is: " + this.age);
        }
    }
    Parent.prototype.sayParent = function(){
        console.log("this is Parent Method");
    }
    
    function Child(firstname){
        Parent.call(this); 
        this.fname = firstname;
        this.age = 40;
        this.saySomething = function() {
            console.log(this.fname);
            this.sayAge();
        }
    }
    
    Child.prototype = new Parent();
    
    var child = new Child("张")
    
    child.saySomething();
    child.sayParent();
    
    console.log(child.hasOwnProperty("sayAge")); // true
    child.sayAge(); //能调用,此时调用的是自身的属性
    delete child.sayAge; // delete只能删除自身的属性,不能删除原型链属性
    console.log(child.hasOwnProperty("sayAge")); // false,自身没有这个属性了
    child.sayAge(); //还能调用,此时调用的是原型链上的方法

如果删掉Parent.call(this), 上面两句child.hasOwnProperty("sayAge"), 都将返回false

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88876.html

相关文章

  • 这一次,我们换种姿势学习 javascript

    摘要:操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。此外可以使用和来设置对象及其属性的不可变性级别。忽视这一点会导致许多问题。使用调用函数时会把新对象的属性关联到其他对象。 前言 《你不知道的 javascript》是一个前端学习必读的系列,让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途。本书介绍了该系列的两个主题:...

    zone 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0
  • JavaScript 五十问——从源码分析 ES6 Class 的实现机制

    摘要:防止类的构造函数以普通函数的方式调用。这个函数的主要作用是通过给类添加方法,其中将静态方法添加到构造函数上,将非静态的方法添加到构造函数的原型对象上。 Class是ES6中新加入的继承机制,实际是Javascript关于原型继承机制的语法糖,本质上是对原型继承的封装。本文将会讨论:1、ES6 class的实现细2、相关Object API盘点3、Javascript中的继承实现方案盘点...

    LeexMuller 评论0 收藏0
  • 前端_JavaScript_面向对象编程

    摘要:面向对象编程对象的原生方法分成两类自身的方法静态方法和的实例方法。的静态方法方法与,参数是对象,返回一个数组,数组的值是改对象自身的所有属性名区别在于返回可枚举的属性,返回不可枚举的属性值。 面向对象编程 Objects对象的原生方法分成两类:Object自身的方法(静态方法)和Object的实例方法。注意Object是JavaScript的原生对象,所有的其他对象都是继承自Objec...

    Blackjun 评论0 收藏0
  • JavaScript面向对象的程序设计

    摘要:目录导语理解对象和面向对象的程序设计创建对象的方式的继承机制原型对象原型链与原型对象相关的方法小结导语前面的系列文章,基本把的核心知识点的基本语法标准库等章节讲解完本章开始进入核心知识点的高级部分面向对象的程序设计,这一部分的内容将会对对象 目录 导语 1.理解对象和面向对象的程序设计 2.创建对象的方式 3.JavaScript的继承机制 3.1 原型对象 3.2 原型链 3.3 与...

    gitmilk 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<