面向对象


面向对象编程(Object Oriented Programming , OOP) 是一种编程范式,它将代码分为具有属性和方法的对象。这种方式的好处是:将相关代码片段封装到对象中,由对象来维护在程序里面的生命状态。对象可以按需被复用或者被修改。

编程范式


在此之前呢先介绍一下,什么叫做编程范式。

所谓编程范式(programming paradigm),指的是计算机编程的基本风格或典范模式。借用哲学的属于,如果说每一个编程者都在创造虚拟世界,那么编程范式就是他们置身其中自觉不自觉采用的世界观和方法论。

在我们的编程语言里面,根据编程范式来分类大致可以分为两大类:命令式编程声明式编程

1.命令式编程

一直以来,比较流行的都是命令式编程。所谓命令式编程,就是以命令为主,给机器提供一条又一条的命令序列让其原封不动的执行。程序执行的效率取决于执行命令的数量。

一句话概括:命令式编程就是命令**"机器"如何去做事情,这样不管你想要的是什么(what),它都会按照你的命令实现。**

我们常见的命令式编程有C语言,C++,Java,C#

2.声明式编程

所谓声明式编程就是告诉**“机器” 你想要什么,让机器想出如何去做(how)**

在声明式编程里面又可以分为2个大类:领域专用语言和函数式编程。

领域专用语言:

英语全称为 domain specific language ,简称SDL。主要是指对一些对应专门领域的高层编程语言,和通用编程语言的概念相对。DSL对应的专门领域(Domain)一般比较狭窄,或者对应某个行业,或者对于某一类具体应用程序,比如数据库等。我们常见的有HTML、CSS、SQL等。

函数式编程:

所谓函数式编程,简单来讲,就是一种编程模型,将计算机运算看作是数学中函数的计算。在函数式编程中,函数是基本单位,是第一型,它几乎被用做一切,包括最简单的计算,甚至连变量都别计算索取代。在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。

随着web开发中高并发的需求越来越多,多核并行的程序涉及被推倒前线,而传统的命令式编程天生的缺陷使得并行编程模型变得非常复杂,使程序员不堪其重。函数式编程在解决兵法程序上面却有着天然的优势,代码简洁优雅,但是缺点就是学习门槛高。

面向对象

一、面向对象基本特征

  1. 封装:也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
  2. 继承:通过继承创建的新类称为“子类”或“派生类”。继承的过程,就是从一般到特殊的过程。
  3. 多态:对象的多功能,多方法,一个方法多种表现形式。
  4. Javascript是一种基于对象(object-based)的语言。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)—–es6以前是这样的。所以es5只有使用函数模拟的面向对象。

二、对象实例化方式

  1. 原始模式:这样的写法有两个缺点,一是如果多生成几个(100个!)实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出没有什么联系。
var Car = {color: 'red',//车的颜色wheel: 4,//车轮数量
}
var Car2 = {color: 'blue',wheel: 4,
}
alert(Car.color);//red
  1. 原始模式的改进:通过写一个函数,解决代码重复的问题。
function createCar(color,wheel) {return {color:color,wheel:wheel}
}
//然后生成实例对象,就等于是在调用函数:
var cat1 = createCar("红色","4");
var cat2 = createCar("蓝色","4");alert(cat1.color);//红色
  1. 工厂模式
function createCar(color,wheel){//createCar工厂var obj = new Object;//或obj = {} 原材料阶段obj.color = color;//加工obj.wheel = wheel;//加工return obj;//输出产品
}
//实例化
var cat1 = createCar("红色","4");
var cat2 = createCar("蓝色","4");alert(cat1.color);//红色
  1. 构造函数模式:为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。 所谓”构造函数”,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。加new执行的函数构造内部变化:自动生成一个对象,this指向这个新创建的对象,函数自动返回这个新创建的对象
function CreateCar(color,wheel){//构造函数首字母大写//不需要自己创建对象了this.color = color;//添加属性,this指向构造函数的实例对象this.wheel = wheel;//添加属性//不需要自己return了
}//实例化
var cat1 = new CreateCar("红色","4");
var cat2 = new CreateCar("蓝色","4");
alert(cat1.color);//红色

三、构造函数注意事项

  1. 此时CreateCar称之为构造函数,也可以称之类,构造函数就是类 。
  2. cat1,cat2均为CreateCar的实例对象。
  3. CreateCar构造函数中this指向CreateCar实例对象即 new CreateCar( )出来的对象。
  4. 必须带new 。
  5. 构造函数首字母大写,这是规范,官方都遵循这一个规范,如Number() Array()。
  6. contructor:这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数,即CreateCar。
alert(cat1.constructor == CreateCar); //true
alert(cat2.constructor == CreateCar); //true
  1. 每定义一个函数,这个函数就有一个 prototype 的属性{},proto 指向被实例化的构造函数的prototype,prototype默认带constructor属性,constructor指向构造函数。
    instanceof 运算符:object instanceof constructor运算符,验证构造函数与实例对象之间的关系。
alert(cat1 instanceof CreateCar ); //true
alert(cat2 instanceof CreateCar ); //true

四、构造函数的问题

构造函数方法很好用,但是存在一个浪费内存的问题。如果现在为其再添加一个方法showWheel。那么,CreateCar就变成了下面这样,这样做有一个很大的弊端,对于每一个实例对象,showWheel都是一模一样的内容,每一次生成一个实例,都必须生成重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

function CreateCar(color,wheel){this.color = color;this.wheel = wheel;this.showWheel = function(){//添加一个新方法alert(this.wheel);}
}//还是采用同样的方法,生成实例:
var cat1 = new CreateCar("红色","4");
var cat2 = new CreateCar("蓝色","4");alert(cat1.showWheel == cat2.showWheel); //false

五、Prototype 原型

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。 这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。__proto__是原型链,指向实例化的函数原型。

function CreateCar(color,wheel){//属性写构造函数里面this.color = color;this.wheel = wheel;
}//方法写原型里面
CreateCar.prototype.showWheel = function(){alert(this.wheel);
}
CreateCar.prototype.showName = function(){alert('车');
}//生成实例。
var cat1 = new CreateCar("红色","4");
var cat2 = new CreateCar("蓝色","4");
cat1.showName();//'车'//这时所有实例的showWheel属性和showName方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
alert(cat1.showWheel == cat2.showWheel );//true
alert(cat1.showName == cat2.showName );//true
console.log(cat1.__proto__ === CreateCar.prototype); //true

六、对象和函数的关系

对象是由函数构造出来的。

Object是Function 的一个实例。

Object.constructor  == Function  //true

函数是Function 的实例,但不是Object 的实例。

function fn(){}
fn.constructor  == Function  //true
fn.constructor  == Object    //false

{} 与 Object 的关系。

var obj = {};
obj.constructor  === Object   //true

七、静态方法和静态属性

只属于类而不属于实例化对象

function foo(){this.show = function(){return this;}
}foo.test = 123; //静态属性foo.say = function(){return this;
}
foo.say();var fn = new foo(); //实例化的新的对象,this指向这个新的对象,不能访问类的静态方法
fn.say(); //Noname1.html:45 Uncaught TypeError: fn.say is not a function
console.log(foo.say() == fn.say());

八、对象继承

利用call()及for in继承 。
给对象的constructor.prototype添加方法属性,对象就会继承,如果要实现一个对象继承其他对象,采用如下方法。

//人类
function Person(name,age){this.name = name;this.age = age;
}
Person.prototype.run = function(){console.log('跑路~')
};
Person.prototype.say = function(){console.log('说话~')
};console.log(Person.prototype);//男人
function Man(){this.sex = "男";
}Man.prototype = Person.prototype;Man.prototype.yyy = function(){console.log('嘤嘤嘤');
}
//会发现Person的prototype也改变了,因为复杂对象的赋值操作是引用而不是赋值
console.log(Person.prototype);
//人类
function Person(name,age){this.name = name;this.age = age;
}
Person.prototype.run = function(){console.log('跑路~')
};
Person.prototype.say = function(){console.log('说话~')
};console.log(Person.prototype);//男人
function Man(){this.sex = "男";
}for(var key in Person.prototype){Man.prototype[key] = Person.prototype[key];console.log(key)
}
Man.prototype.yyy = function(){console.log('嘤嘤嘤');
}console.log(Person.prototype);
var xm = new Man();
xm.yyy();
采用中介
function ClassA(name){this.name = name;
}
ClassA.prototype.say = function(){console.log(666);
}//中继来做准备工作
function Ready(){}//
Ready.prototype = ClassA.prototype;//引用//需要来继承ClassA
function ClassB(){}
ClassB.prototype = new Ready();//new 返回了一个新对象 __proto__指向被实例化的构造函数的prototype
ClassB.prototype.constructor = ClassB;
console.log(ClassB.prototype);
采用中介,使用call改变this指向
function ClassA(name){this.name = name;
}
ClassA.prototype.showName = function(){console.log(this.name);
}//中继来做准备工作
function Ready(){}//
Ready.prototype = ClassA.prototype;//引用//需要来继承ClassA
function ClassB(name){ClassA.call(this,name);
}
ClassB.prototype = new Ready();//new 返回了一个新对象 __proto__指向被实例化的构造函数的prototype
ClassB.prototype.constructor = ClassB;
console.log(ClassB.prototype);
var xiaoming = new ClassB('小明');
xiaoming.showName();

九、多态

同一个方法,面对不同的对象有不同的表现形式就叫做多态。

var obj = {eat : function(_type){if(_type == '猫'){console.log('猫粮')}else if (_type == "狗") {console.log('狗粮')}else{console.log("吃饭");}}
};
obj.eat("狗");

十、hasOwnProperty

查看该属性是否在这个对象本身上,只有在自身属性上才会返回真,在原型链上会返回假。

function ClassA(){}
ClassA.prototype.test = function(){console.log('test')
}var a = new ClassA();
a.test();
console.log(a.hasOwnProperty('test')); //false

十一、描述符(修饰符)

描述符是对一个属性的特性的描述,defineProperty设置描述符(修饰符),value设置属性值,configurable是否允许修饰符被改变 默认为false,enumerable 是否可以被枚举 默认为false,writable 是否可以被 = 等号改变 默认为false。

var obj = {a : 1
};
var c = 666;
Object.defineProperty(obj,'c',{//value : 233,//enumerable : false,//writable : true,//他的值能否改变//设置的时候调用set : function(n){//n 就是等号的右边的值c = c*n;},//获取的时候调用get : function(){return c;},configurable : true,//是否可以再次修改修饰符
});

面向对象总结

  1. 所谓编程范式,指的是计算机编程的基本风格或典范模式。大致可以分为命令式编程和声明式编程。
  2. 面向对象的程序设计是站在哲学的角度上,将人类思维融入到了程序里面的一种编程范式。
  3. 描述对象的时候可以通过两个方法来进行描述。分别是对象的外观和功能。在程序中与之对应的就是属性和方法。
  4. JavaScript中的对象都是基于原型的,从一个原型对象中可以克隆出一个新的对象。
  5. 在JavaScript中每个对象都有一个原型对象。可以通过__proto__属性来找到一个对象的原型对象。
  6. 在其他语言中,对象从类中产生。而JavaScript中,通过构造函数来模拟其他语言中的类。
  7. 类与对象的关系可以总结为:类是对对象的一种概括,而对象是类的一种具体实现。
  8. 面向对象的三大特征:封装、继承、多态
  9. 封装是指对象的属性或者方法隐藏于内部,不暴露给外部。
  10. 继承是指一个子类继承一个父类。在继承了父类之后,子类就拥有了父类的所有属性和方法。
  11. 多态是指不同的对象可以拥有相同的方法,不过是以不同的方式来实现它。
  12. this的指向是根据使用的地方不同分为好几种情况,但是我们可以通过一些方式来修改this的指向。

执行上下文

  • 先进后出,后进先出。
函数的执行上下文的生命周期

执行上下文的生命周期分为两个阶段:

  1. 创建阶段(进入执行上下文):函数被调用时,进入函数环境,为其创建一个函数上下文
  2. 执行阶段(代码执行):执行函数中代码时,此时执行上下文进入执行状态。
创建阶段的操作
  1. 创建变量对象
  • 函数环境会初始化创建 Arguments 对象,形式参数(并赋值)。
  • 普通函数声明(并赋值)
  • 局部变量声明,函数表达式声明(未赋值)
  1. 初始化作用域链
  2. 确定this指向(this由调用者确定)
  3. 确定作用域(词法环境决定,哪里声明定义,就在哪里确定)
执行阶段的操作
  1. 变量对象赋值
  • 变量赋值
  • 函数表达式赋值
  1. 调用函数
  2. 按顺序执行其他代码
执行上下文与作用域区别

作用域和执行上下文不是同一个概念。

执行全局代码时,会产生一个执行上下文环境,每次调用函数都会执行上下文环境。当函数调用完时,这个上下文环境以及其中的数据都会被消除(除了闭包),处于活动状态的执行上下文环境只有一个

而作用域在函数定义时就已经确定了,不是在函数调用时确定(区别于执行上下文环境,当然this也是上下文环境里的成分)

// 全局作用域
let x = 100;
function bar() {    console.log(x);
}
// fn作用域
function fn() {  let x = 50;    // bar作用域bar();
}
fn(); // 100

作用域只是一个"地盘",其中没有变量。变量时哦通过作用域对应的执行上下文环境中的变量对象来实现的。所以作用域是静态观念的,而执行上下文环境是动态上的,两者并不一样。

变量对象

执行上下文抽象成为了一个对象,拥有个属性,分别是变量对象,作用域链以及this指向

  • 变量对象里面所拥有的东西
  1. Arguments对象
  2. 确定形式参数,检查上下文中函数声明
  3. 找到每一个函数声明
  • 就在 variableObject 下用函数名创建一个属性,属性值就指向该函数在内存中的地址的一个引用。
  1. 确定当前上下文中的局部变量,如果遇到和函数名同名的变量,则会忽略该变量
  • 通过例子来演示函数的这两个阶段以及变量对象是如何变化的。
const foo = function(i){   var a = "Hello";  var b = function privateB(){};    function c(){} }
foo(10);//首先在建立阶段
fooExecutionContext = {    variavleObject : {
arguments : {0 : 10,length : 1
}, // 确定arguments对象
i : 10, // 确定形式参数
c : pointer to function c(), // 确定函数的引用
a : undefined, // 局部变量 初始值为 undefined
b : undefined  // 局部变量 初始值为 undefined}, scopeChain : {},  this : {}}

14-2闭包(closure)

闭包:

  • 一个函数中要嵌套一个内部函数,并且内部函数要访问外部函数的变量
  • 内部函数要被外部引用
function eat(){var food = '鸡翅';
return function(){console.log(food);
} }var look = eat(); look(); // 鸡翅look(); // 鸡翅
//eat函数返回一个函数,并在这个内部函数中访问food这个局部变量。调用eat函数并将结果look,这个look指向了eat函数中的内部函数,然后调用它,最终输出food值

自由变量

  • 自由变量可以理解成跨作用域的变量,比如子集作用域访问作用域的变量。
    例:
var school = function () {var s1 = "小强"
var s2 = "小王";
var team = function (project) {console.log(s1 + s2 + project);
}return team;
} var team = school();
team("做电商项目"); // 小强小王做电商项目
team("做微信项目"); // 小强小王做微信项目
//变量s1和s2属于school函数的局部变量,并被内部哈桑农户team使用,同时该函数也被返回出去在外部,通过调用school得到了内部函数的引用,后面多次调用这个内部函数,仍然能够访问到s1和s2变量被team函数引用,即使创造它们的函数school执行完了,这些变量依然存在,这就是闭包

3:闭包的原理

因为作用域对象包含该上下文中的 VO/AO对象,还有scope对象,所以内部函数可以访问到函数的变量

4:闭包的优缺点

优点:

  • 通过闭包可以让外部环境访问到函数内部的局部变量。
  • 通过闭包可以让局部变量持续保存下来,不随着它的上下文环境一起销毁。
    缺点:
  • 局部变量本来应该在函数退出时被解除引用,但如果局部变量被封印在闭包形成的环境中,那么这个局部变量就一直能生存下去,然而闭包的确会使一些数据无法被及时销毁。

函数节流

  • 目的:是为了防止一个函数短时间内被频繁的触发。
  • 原理:是将即将执行的函数用setTimeout()延迟一段时间执行。而函数节流的原理是让连续的函数执行,变为固定时间段间断的执行

1:使用时间戳

  • 触发事件时,取出当前的时间戳,然后减去之前的时间戳(最一开始值设为0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。
    例:
function throttle(func, wait) {let context, args;
let previous = 0;
return function () { let now = +new Date(); context = this;args = arguments;if (now - previous > wait) {func.apply(context, args);previous = now; } }
}

2:使用定时器

  • 触发事件时,设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,知道定时器执行,然后执行函数,清空定时器,这样就可以设置一个定时器。
    例:
function throttle(func, wait) {let timeout, args, context;let previous = 0;return function () {context = this;args = arguments; if (!timeout) {timeout = setTimeout(function () { timeout = null; func.apply(context, args)
}, wait); } }
}

14-3(分时函数)

timeChunk()函数接受3个参数。第一个参数是创建节点时需要用到的数据,第二个参数是封装了创建节点逻辑的函数,第三个参数表示每一批创建的节点数量

<body>
<script>
const timeChunk = function (ary, fn, count) {let obj, t;
let len = ary.length;
const start = function () {
for (let i = 0; i < Math.min(count || 1, ary.length); i++) { let obj = ary.shift();
fn(obj); }
};
return function () {t = setInterval(function () {//如果全部节点都已经被创建好
if (ary.length === 0) {return clearInterval(t);  }
start();
}, 200); //分批执行的时间间隔,也可以用参数的形式传入
};
};
//最后进行一些测试,假设我们有1000个好友的数据
// 然后利用timeChunk函数,每一批直往页面中创建8个节点const ary = []; for (let i = 1; i <= 1000; i++) { ary.push(i); } const renderFriendList = timeChunk(ary, function (n) {            const div = document.createElement('div');           div.innerHTML = n;            document.body.appendChild(div);}, 8); renderFriendList();</script>
</body>

可以看到1000条数据是分批创建添加的
##14-4(惰性函数)
在Web开发中,因为浏览器之间实现的差异,一些嗅探工作总是不可避免的,比如需要在各个浏览器中能够通用的事件绑定函数addEvent()

解决方式:

  • 惰性载入函数方法
    此时add Event()依然被声明为一个普通函数,在函数里依然有一些分支判断。但是在第一次进入条件分支之后,在函数内部会重写这个函数,重写之后的函数就是我们所期望的add Event()函数,在下一次进入时它不再存在条件分支
<body>
<div id="div1">点我绑定事件</div>
<script>const addEvent = function (elem, type, handler) {            if (window.addEventListener) {addEvent = function (elem, type, handler) {   elem.addEventListener(type, handler, false);}
}
if (window.attachEvent) {addEvent = function (elem, type, handler) {                elem.attachEvent('on' + type, handler);
}
}
addEvent(elem, type, handler);
// 第一次绑定事件的时候需要手动调用以下}; const div = document.getElementById('div1');        addEvent(div, 'click', function () { alert(1);});addEvent(div, 'click', function () { alert(2);}); </script> </body>

js面向对象以及编程范式 js高级部分相关推荐

  1. 面向对象:编程范式、类、对象

    编程范式: 1. 面向过程编程: 核心是"过程","过程"指的是解决问题的步骤:就相当于在设计一条流水线 优点:复杂问题流程化,进而简单化 缺点:可扩展性差,前 ...

  2. 基于对象和面向对象编程范式辨析和主流编程语言中的应用

    基于对象和面向对象编程范式辨析和主流编程语言中的应用 前言 本文的目的是想告诉大家,为什么C++的模板这么强大.为什么Ruby的Duck Typing(像鸭子那样编程)这么强大! 基于对象和面向对象编 ...

  3. python标准库很丰富支持多种编程范式_基础班-第03天{python基础}

    声明:内容来源于网络,本人只是在此稍作整理,如有涉及版权问题,归传智播客官方所有. 认识python(了解) 1.Python发展历史 起源 Python的作者,Guido von Rossum,荷兰 ...

  4. Programming Paradigms 编程范式-笔记

    Stephen Covey的<The 7 Habits of Highly Effective People>一书中,有一个关于paradigm shift(范式转移)的小故事: 军舰在雾 ...

  5. Python面向对象的编程

    提示:本文代码完全来源于哔哩哔哩<10小时搞定Python面向对象(完整版)>,本文用途仅限于Python编程的学习. 网址:https://www.bilibili.com/video/ ...

  6. java实现选项卡定时轮播_原生js面向对象编程-选项卡(自动轮播)

    原生js面向对象编程-选项卡(自动轮播) }#div1 input{color:#fff;width:100px;height:40px;background:darkseagreen;border: ...

  7. 简单粗暴地理解js原型链–js面向对象编程

    简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...

  8. JS面向对象编程(OOP)

    什么是JS面向对象编程(OOP)? 用对象的思想去写代码,就是面向对象编程. 上面这张图就是一个对象,紫色部分就是车的属性,黄色部分就是修改车的方法: 把他们集合到一个构造函数内,就是这样的 func ...

  9. JS 面向对象编程、原型链、原型继承(个人学习总结)

    一.面向对象 1. 面向对象 是所有语言 都有的一种编程思想,组织代码的一种形式 基于对象的语言:JS语言 面向对象的语言:c++ java c# 2. 面向对象 3大特征 封装:将重用代码封装到函数 ...

最新文章

  1. 学web前端需要了解哪些常识
  2. Linux课程设计八音盒,单片机课程设计——八音盒精要.doc
  3. 使用 kubeadm 创建一个 kubernetes 集群
  4. cms php vue 开源_骑士CMS文件包含+getshell
  5. burp suite 二级域名扫描插件
  6. go语言json字符串解析为结构体数组,结构体指针的数组
  7. 【操作系统】连续内存分配策略
  8. 从Python 2切换到Python 3:您需要了解的内容
  9. linux 下搭建subversion (svn),CentOS Linux 搭建 SVN(CollabNet Subversion)服务器,易用性和对远程团队的...
  10. hdu1166 经典线段入门
  11. python opencv 录制视频_python - 使用Opencv Python多线程录制视频 - 堆栈内存溢出
  12. FastAPI用户安全性解决方案
  13. yii2 表单提交没有对应的路由参数解决办法
  14. 简易java电子词典_使用Android简单实现有道电子词典
  15. SpringMVC 访问html页面乱码
  16. 硬件-电子基础元器件(一)电阻
  17. 尼日利亚4g频段_世界各国全球主要4g频段资料
  18. 杭州-SQL杭州国迈软件有限公司笔试题
  19. JPA criteria 查询:类型安全与面向对象
  20. 无准考证号的四六级查询

热门文章

  1. vuex导入_vuex 引用方法
  2. 记事本打开文本文件不换行的问题
  3. 冬季-丽江自助游攻略
  4. Pygame中的Sprite类
  5. 撞库、爬虫、蜜罐、网络钓鱼、DDoS、僵尸网络、暗网
  6. python编译器安装教程
  7. 淘宝开源网络框架tbnet之transport篇
  8. 客户端渗透之——浏览器渗透
  9. ziheng -q登录界面
  10. python股票交易策略_Python:股票演算法交易實務 145個關鍵技巧詳解