面向对象编程思想(前传)--你必须知道的javascript

什么是鸭子类型

javascript是一门典型的动态类型语言,也就弱类型语言。
那什么是鸭子类型:【如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子】

var 鸭子 = {
    走路: function () { },
    咕咕咕: function () { }
}

var 鹦鹉 = {
    走路: function () { },
    咕咕咕: function () { }
}

这只鹦鹉同样有“走路”和“咕咕咕”的方法,那在js的世界里就可以把它当成鸭子。
可以这样调用:

var 鸭子们 = [];
鸭子们.push(鸭子);
鸭子们.push(鹦鹉);

for (var i = 0; i < 鸭子们.length; i++) {
    鸭子们[i].走路();
}

所以js的世界没有抽象和接口,但可以约定“我们都是鸭子”。

javascript的面向对象

javascript不仅是直译式脚本语言、动态类型、弱类型语言、函数为一等公民的语言,它还是基于原型的面向对象语言。面向对象三大特性:封装、继承、多态,下面我们用js分别实现。

封装

var Person = (function () {
    var sex = "纯爷们";
    return {
        name: "农码一生",
        getInfo: function () {
            console.log("name:" + this.name + ",sex:" + sex);
        }
    };
})();


虽然老的js语法没有提供private等关键字,但是我们可以利用闭包来实现私有字段,达到封装的目的。

继承

  • 字面量表示:

    var Person = {
    name: "农码一生",
    getName: function () {
        console.log(this.name);
    }
    };
    var obj = Person;
    obj.getName();

  • 函数构造器:
var Person = function () {
    this.name = "农码一生";
}
Person.prototype.getName = function () {
    console.log(this.name);
}

var obj = function () { };
obj.prototype = new Person();//obj继承于Person

var o = new obj();
o.getName();//直接调用原型中的getName(类似于C#中的调用父类方法)

多态

对于多态,其实上面的鸭子类型已经表现的很清楚了。

var 鸭子们 = [];
鸭子们.push(鸭子);
鸭子们.push(鹦鹉);

for (var i = 0; i < 鸭子们.length; i++) {
    鸭子们[i].走路();//对于鹦鹉来说,它可能是跳着走。对于鸭子来说,它可能左右摇摆着走。这就是多态的表现。
}

对于鹦鹉来说,它可能是跳着走。对于鸭子来说,它可能左右摇摆着走。这就是多态的表现。

原型

什么是原型?在js中是没有类的,那它怎么创建对象。在C#中我们可以通过new关键字实例化一个对象,在js中我们用new关键字构造一个原型对象。C#中一切对象继承于Object,js中一切对象的原型是Object。

var Person = function () {
    this.name = "农码一生";
    this.sex = "纯爷们";
};
console.log(Person.prototype);


我们很多时候给一个对象添加方法的时候就是写在原型上,这是为什么?直接写在对象里会有问题吗?下面我们试试:

var Person = function () {
    this.name = "农码一生";
    this.sex = "纯爷们";
    this.getInfo = function () {
        console.log("name:" + this.name + ",sex:" + this.sex);
    }
};


好像并看不出什么问题。其实不然...

我们发现,每次构造出来的对象中的方法都会去开辟一个空间。但是对象的方法都是一样的,完全没有必要。 我们可以把方法放入原型。

这样一来,不过我们构造多少对象,其方法都是公用的(单例的)。
可是为什么会这样呢?
首先,想想原型这个词,很形象,原本的模型。我们来看一个继承的例子:

var Person = function () {
    this.name = "农码一生";
    this.sex = "纯爷们";
    this.getInfo = function () {
        console.log("name:" + this.name + ",sex:" + this.sex);
    }
};
var Student = function () { };
Student.prototype = new Person();//继承
var s1 = new Student();
var s2 = new Student();
console.log(s1.getInfo === s2.getInfo);


虽然getInfo在Person里面是直接实现的,但是到了Student的原型(prototype)里面就是一个Person对象的单例了。也就是说无论构造多少个Student对象其中的getInfo方法都是同一个。
但是,构造多个Person就有多个getInfo方法。所以,我们应该把getInfo方法放入Person的原型中。

var Person = function () {
    this.name = "农码一生";
    this.sex = "纯爷们";
};
Person.prototype.getInfo = function () {
    console.log("name:" + this.name + ",sex:" + this.sex);
};

我们仔细推敲下这句话“把getInfo方法放入Person的原型中”,Person的原型是Object,那也就是说getInfo方法放到Object里面去了?
是的,不信请看:

如果原型和原型的原型都实现了同样的方法呢?我们来猜猜下面会打印哪个版本

var Person = function () {
    this.name = "农码一生";
};
var Student = function () { };
Student.prototype = new Person();//继承
var stu = new Student();

Student.prototype.getName = function () {
    console.log("我的名字:" + this.name);
}
Person.prototype.getName = function () {
    console.log("My name is:" + this.name);
}

stu.getName();

如果注释掉中文版呢?

有没有觉得特神奇,具体原因我们用图来回答:

从另个一角度说,如果对象实现了原型中已有的方法那就等效于C#中虚方法重写了。

this指向

var name = "张三";
var obj = {
    name:"李四",
    getName: function(){
        console.log(this.name);
    }
}

obj.getName();


这个结果大家应该没什么疑问。
接着看下面的:

window.name = "张三";
var obj = {
    name:"李四",
    getName: function(){
        console.log(this.name);
    }
}
//obj.getName();
window.func = obj.getName;
window.func();


晕了没有?没关系,告诉大家一个简单实用的方法:方法是被谁“.”出来的,this就指向的谁。

call

"方法是被谁“.”出来的,this就指向的谁",这个口诀不一定适用所有方法。为什么这么说呢?请看下面:

window.name = "张三";
var obj = {
    name: "李四",
    getName: function () {
        console.log(this.name);
    }
}
//obj.getName();
window.func = obj.getName;
window.func.call(obj);


虽然还是window点的,但this已经指向了obj。
因为call可以改变this执行。
这个特性非常有用。比如,我们要编写一个下拉选中事件。

function func() {
    console.log("我点击了" + $(this).find("option:selected").text());
}

$("#element1").change(function () {
    func.call(this);
});
$("#element2").change(function () {
    func.call(this);
});

在写func方法的时候不用考虑具体是那个下拉框元素。

apply

apply和call区别不大。

function func(age, sex) {
    console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);
}

var obj = {
    name: "晓梅"
}

func.call(obj, "18", "妹子");
func.apply(obj,["18","小美女"]);


call和apply第一个参数都是this指向的对象。call第二个和以后的参数对应方法func的参数。而apply的第二个参数是个数组,包含方法的所有参数。

band

function func(age, sex) {
    console.log("name:" + this.name + ",age:" + age + ",sex:" + sex);
}

var obj = {
    name: "晓梅"
}
var func1 = func.bind(obj, "18", "妹子");
func1();

和apply、call的区别是,只是改变this指向并不执行。且参数传入方式和call一样。

js中的闭包

什么是闭包?我的理解是存在不能被回收的变量就是闭包。
最常见最大的一个闭包就是全局变量,定义了就不会被销毁,除非自动设为null。
而我们平时说的和使用的闭包却非如此,但同样会产生不会被销毁的变量。比如我们之前说的私有变量示例:

var Person = (function () {
    var sex = "纯爷们";
    return {
        name: "农码一生",
        getInfo: function () {
            console.log("name:" + this.name + ",sex:" + sex);
        }
    };
})();

之所以说它是闭包,那是因为sex这个字段是永远不会被销毁。你想想,如果被销毁了,那我们调用getInfo的时候岂不是找不到sex字段了。所以不是不会销毁,而是不能销毁。
闭包的作用不仅仅是私有化。我们再来一例:

for (var i = 0; i < 10; i++) {
    var t = setTimeout(function () {
        console.log(i);
    }, 100);
}


并不是我们想象的那样打印0到9。
因为计时器还没开始循环就执行完了。而此时变量i已经是10。
我们可以通过闭包为每次循环保存一个闭包变量。

for (var i = 0; i < 10; i++) {
    (function (i) {
        var t = setTimeout(function () {
            console.log(i);
        }, 100);
    })(i);
}

什么是高阶函数

“高阶函数”名字特牛逼。其实我们在js中经常使用。
还是私有变量的例子:

var Person = (function () {
    var sex = "纯爷们";
    return {
        name: "农码一生",
        getInfo: function () {
            console.log("name:" + this.name + ",sex:" + sex);
        }
    };
})();
  • 当函数做被return时,那么就是高阶函数。
var getInfo = function (callback) {
    $.ajax(‘url‘, function (data) {
        if (typeof callback === ‘function‘) {
            callback(data);
        }
    });
}
getInfo(function (data) {
    alert(data.userName);
});

getInfo在执行的时候,传入的参数是个函数。

  • 当函数被当成参数传递时,那么这也是高阶函数。

-转载

时间: 2025-01-04 22:48:46

面向对象编程思想(前传)--你必须知道的javascript的相关文章

面向对象编程思想(前传)--你必须知道的javascript(转载)

原文地址:http://www.cnblogs.com/zhaopei/p/6623460.html阅读目录 什么是鸭子类型 javascript的面向对象 封装 继承 多态 原型 this指向 call apply band js中的闭包 什么是高阶函数 在写面向对象编程思想-设计模式中的js部分的时候发现很多基础知识不了解的话,是很难真正理解和读懂js面向对象的代码.为此,在这里先快速补上.然后继续我们的面向对象编程思想-设计模式. 什么是鸭子类型 javascript是一门典型的动态类型语

《jQuery风暴》第2章 必须知道的JavaScript知识

第2章 必须知道的JavaScript知识 JavaScript是jQuery应用的基础,掌握JavaScript这门语言是使用jQuery的基础条件.本章不会全面细致的讲解JavaScript的全部, 而是讲解其精髓,这些知识可以提升大家的JavaScript内功.切忌,要修炼上乘的武功,必须要有深厚的内功基础,否则只可学到其招式而发挥不了功力.JavaScript实际上包括三部分: w   ECMAScript 描述了该语言的语法和基本对象. w   DOM 描述了处理网页内容的方法和接口.

从零开始学习jQuery(剧场版) 你必须知道的javascript

原文:从零开始学习jQuery(剧场版) 你必须知道的javascript 一.摘要 本文是jQuery系列教程的剧场版, 即和jQuery这条主线无关, 主要介绍大家平时会忽略的一些javascript细节.  适合希望巩固javascript理论知识和基础知识的开发人员阅读.   二.前言 最近面试过一些人, 发现即使经验丰富的开发人员, 对于一些基础的理论和细节也常常会模糊. 写本文是因为就我自己而言第一次学习下面的内容时发现自己确实有所收获和感悟.  其实我们容易忽视的javascrip

五个你必须知道的javascript和web debug技术

转:http://js8.in/2013/11/20/%E4%BA%94%E4%B8%AA%E4%BD%A0%E5%BF%85%E9%A1%BB%E7%9F%A5%E9%81%93%E7%9A%84javascript%E5%92%8Cweb-debug%E6%8A%80%E6%9C%AF/ 在前端开发中,调试技术是必不可少的技能,本文将介绍五种前端开发必备的调试技术. Weinre移动调试 DOM 断点 debugger断点 native方法hook 远程映射本地调试 Weinre 在移动上面

细说Cocos2d-JS——你必须知道的JavaScript

对于使用游戏引擎开发游戏而言,你选择何种语言并不重要,重要的是你对这个游戏引擎的理解和掌握,你对开发游戏的实践和感悟,毕竟一种游戏引擎常常支持很多语言开发,不同的项目使用的语言也可能很不一样. --有些人说 这个观点确实不无道理,但是,我依旧认为,无论是对于Cocos2d还是Unity3d抑或是其他的游戏引擎,无论你选择的是C++,C#,Lua还是JavaScript,在对游戏引擎深入研究之前或者之外,对你所选语言的研究显得尤为重要.这可能会给你带来质的提升. 一.JavaScript--想说爱

《你必须知道的javascript(上)》- 2.this与对象原型

1.1 为什么使用this 随着你的使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样.当我们介绍对象和原型时,你就会明白函数可以自动引用合适的上下文对象有多重要. 1.2 关于误解 首先需要消除一些关于this的错误认识. 1.2.1 指向自身 先来看个例子: function foo(num) { console.log("foo: " + num); // 记录foo被调用的次数,这里this指向window this.count++; } fo

第三章面向对象编程思想

""" 编码规范: 类名首字母应以大写字母开头 类的成员/属性: 成员属性 实例化对象的属性: self.name=name 可以被访问,也可以在类的外部被修改 私有属性 实例化对象的私有属性: self.__age=age 属性名前加两下划线 外部无法被访问, 也不能被修改,只能内部访问和修改 想要在外部访问只能自定义一个方法了 强制访问 私有属性: p._People__age 对象名._类名__私有属性 公共属性: 类的属性,也叫静态属性,他是在类的内部定义的 clas

面向对象编程思想(OOP)

本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞大 很多软件进入维护阶段 需求的不断变更 软件开发中存在很多其他的问题,上面只是从程序开发和设计的角度看到的部分问题.需求解决上面软件开发中的问题,就要求我们编写(设计)的软件具有很好的可读性.可维护性和可扩展性.我们需要保证代码具有高内聚低耦合. 下面将简单介绍面向对象的一些基本特性.设计原则,以

面向对象编程思想的哲学起源(转载)

http://www.xuebuyuan.com/566309.html 本来想象着写一整篇「面向对象编程思想的哲学起源」这样的题目,笔走纸上,方才发现这样的题目足够出本书,知识不够,写不动.但心里还是想写点自己的所思所想. 全篇就拿JAVA来举例了.众所周知,面向对象的四大基本要素:抽象(Abstract).封装(Encapsulation).继承(Inheritance).多态(Polymorphism). 很多人坚持<逻辑学>是唯物哲学的基础,不懂,姑且不论.哲学就是对自然学科的抽象,看