原型继承和回调函数

prototype与自定义类

任何一个函数都有prototype属性。
访问自带的构造函数Array的原型
Array.prototype //访问结果为 [ ]
访问自定义构造函数Robot的原型

function Robot(name) {
    this.name = name;
    this.say = function(){
                console.log(this.name)
             }
};
Robot.prototype = { age: 12, gender: "boy" }    //设置构造函数Robot的
原型对象(属性值可以为函数)
Robot.prototype        //Object { age: 12, gender: "boy" }

但是一般只有当把一个函数当构造函数使用时,我们才使用prototype。 上一节讲到,我们自定义类的时候,使用的那种方式是费内存的,我们使用prototype来定义就会不同。

function Car(x,y){ 
        this.x = x;
        this.y = y;

}

Car.prototype.run = function(x_increase, y_increase){
    this.x += x_increase;
    this.y += y_increase;
}
Car.prototype.show = function(){
    console.log("( " + this.x + "," + this.y + " )");
}

这样当我们new Car的时候,run和show并不会每次都分配内存,所以会比较省内存。
当我们得到一个实例之后,访问实例的原型,使用proto属性.(注:proto不是标准属性,但是被大多数浏览器支持.)

var Robot = function(name) {
                this.name = name;
                this.say = function(){
                            console.log(this.name)
                         }
                };
Robot.prototype = { age: 12, gender: "boy" }    //设置构造函数Robot的原型对象(属性值可以为函数)
var robot = new Robot("bower")        //实例化一个对象
robot.__proto__        //Object { age: 12, gender: "boy" }

new关键字

当我们,执行

new Robot("bower")
实际上是得到了一个从Robot.prototype继承而来的一个对象。如果要理解原型继承中new的意义,还是这样理解最好。

如果我们要描述new的工作流程,如下:

1.分配一个新的空对象.

2.设置新对象的相关属性、方法,例如继承Robot.prototype上的各式方法、属性。注意,这里执行的并不是拷贝,而是类似于引用的方法,我们叫它代理(比如Robot.prototype对象的属性发生变化时,由Robot生成的实例对象直接继承的相应属性也会变化)。

3.将这个新对象作为构造函数的执行上下文(其this引用指向这个新的对象),并执行构造函数.

4.返回这个对象到执行new Robot("bower")的位置,赋值给前面的变量名.

var Robot = function(name) {        //声明一个构造函数
                this.name = name;
                this.say = function(){
                            console.log(this.name)
                         }
                };
Robot.prototype = { age: 12, gender: "boy" }    //设置构造函数Robot的原型对象(属性值可以为函数)
var robot = new Robot("bower")        //实例化一个对象
当执行new Robot("bower")时

首先,其中Robot.prototype这个对象的值{age:12,gender:"boy"}会被新的空对象继承,所以后面robot.age的值是12,robot.gender的值是"boy".

然后,执行构造函数,运行时执行的方法可以理解为Robot.apply(robot,["bower"])的方式.此时会去设置新对象的属性name="bower";say=function(){this.name}.所以后面robot.name的值是"bower".执行robot.say()会输出"bower".

其实,最后才把新的对象的值赋给变量robot.

原型继承

Javascript对象(如robot)拥有自有属性(如通过构造函数this.name=name设置的属性)和继承属性(例如代理自Robot.prototype的属性)两种。

在查询对象robot的属性age时,先查找robot中自有属性的age属性,如果没找到,则查找robot继承属性(也就是robot的原型对象:robot.proto)中的age属性,直到查找到age或者一个原型是null的对象为止.

在给对象robot的age属性赋值时,如果robot自有属性中已经有一个属性age,则改变age的值,若robot中不存在自有属性age,只存在继承属性age或者所有属性中都没有age属性,则为robot创建一个自有属性age并为其赋值.

也就是说,只有在查询时原型链才会起作用。赋值只针对自有属性.

var Robot = function(name) {        //声明一个构造函数
                this.name = name;
                this.say = function(){
                            console.log(this.name)
                         }
                };
var info = { age: 12, gender: "boy" };
Robot.prototype = info;

var robot = new Robot("bower");

console.log(robot);            //打印结果为Robot {name: "bower", say: function, age: 12, gender: "boy"}
console.log('age' in robot);    //打印结果为true
console.log(robot.hasOwnProperty('age'));    //打印结果为false

robot.name = "cup";
robot.age = 13;

console.log(robot);            //打印结果为Robot {name: "cup", say: function, age: 13, gender: "boy"}
console.log('age' in robot);    //打印结果为true
console.log(robot.hasOwnProperty('age'));    //打印结果为true

可以使用in 或者 hasOwnProperty 来判断对象中是否存在属性或者是否存在自有属性。

原型链

Javascript基于 ‘proto’ 的原型链
我们知道实例对象存在自有属性和继承属性两种,利用原型继承的方法我们有能力创建一个看似矛盾的对象,该对象本身即可以被执行,又可以存取数据.

即创建一个可执行对象.既可以当作对象来使用(有原型链),也可以当作函数来直接调用.

例如

可以执行

robot();
也可以通过

robot.name = "bower"
来给robot对象设置属性值.

function SuperRobot(data) {
    var say = function() { return "Hello World!"; }
    say.__proto__ = data;
    return say;
}

var data = { name:"atom", age: 5};
var super_robot = SuperRobot(data);

console.log(super_robot());         //Hello World!
console.log(super_robot.age);       //5
console.log(typeof super_robot);    //function

回调函数

一个回调函数(例如“function_1”),是一个被作为参数传递给另一个函数(例如“function_2”)的函数,回调函数在function_2中被调用。一个回调函数本质上是一种编程模式(为一个常见问题创建的解决方案),因此,使用回调函数也叫做回调模式。

下面是另一个Javascript中典型的回调函数的例子:

var function_1=function(callback){
    console.log("do something.");
    (callback && typeof(callback) === "function") && callback();
}
var function_2=function(){console.log("function_2 is running.")};
function_1(function_2);
//callback && typeof(callback) === "function" 的作用是判断参数callback是否被传入,并且判断该参数是否是一

上面的代码是将一个具名函数function_2传入函数function_1.其实际效果等同于下面的匿名函数传递.

var function_1=function(callback){
    console.log("do something.");
    (callback && typeof(callback) === "function") && callback();
}
function_1(function(){console.log("function_2 is running.")});

在此,注意到我们将一个匿名函数(没有名字的函数)作为参数传递给了function_1,当function_1中调用执行该匿名函数callback时才会去执行该传入的函数,也就是说,该匿名函数只是在合适的时机去被执行。
使用场合
1.资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调,AJAX等等。

2.DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。 setTimeout的延迟时间为0,这个hack经常被用到,settimeout调用的函数其实就是一个callback的体现

3.链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现

4.setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。

可能你对上面的使用场景并不熟悉,但不必担心,在我们的高级课程和html的相关课程中会有相关知识点的介绍,只要在今后使用时能够意识到"我这是使用了回调模式"就可以了.

到目前为止,我们将函数作为参数传递给了另一个函数或方法。在我们学习更多的实际例子和编写我们自己的回调函数之前,接下来先来理解回调函数是怎样运作的。

回调函数的运行

函数在Javascript中是第一类对象,我们可以像对待对象一样对待函数,因此我们能像传递变量一样传递函数,在函数中返回函数,在其他函数中使用函数。当我们将一个回调函数作为参数传递给另一个函数时,我们仅仅传递了函数的定义,我们并没有在参数中执行该函数。我们并不传递像我们平时执行函数一样带有一对执行小括号()的函数,而只是传递函数名。

var function_1=function(callback){
    var word_1 = "do something.";
    var word_2 = "do another thing.";
    console.log(word_1);
    (callback && typeof(callback) === "function") && callback(word_2);
}
var function_2=function(word){console.log(word)};
function_1(function_2);

需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)。

//全局变量
var info = [];

//普通的show函数,将数据的内容打印到控制台
function show(data){
    info.push(data)
    //如果是可以直接输出的字符串则直接输出
    if ( typeof data === "string")
    {
        console.log(data);
    }
    else if ( typeof data === "object"){
        //遍历data
        for(var item in data){
            console.log(item + ": " + data[item]);
        }
    }
}

//定义一个接收两个参数的函数,参数中后面一个是回调函数, 在函数体中调用回调函数,并将第一个参数传入回调函数.
function get_inputs(object,function_1){
    (function_1 && typeof(function_1)==="function")&&function_1(object);
}


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

推荐阅读更多精彩内容