从零开始学习前端JAVASCRIPT — 12、JavaScript面向对象编程
一、闭包
1 . 概念:闭包就是能够读取其他函数内部变量的函数。在JS中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解为”定义在一个函数内部的函数”。
2 . 闭包的特点
1)可以读取函数内部的变量。
2)让这些变量的值始终保存在内存中。
3 . 闭包的原理
理解闭包,首先必须理解JS变量的作用域。变量的作用域无非就是两种(es5):全局变量和局部变量。
JS语言的特殊之处,就在于函数内部可以直接读取全局变量。另一方面,函数外部自然无法读取函数内的局部变量。
注意:
1)函数内部声明变量的时候,一定要使用var声明。如果不用的话,你实际上声明了一个全局变量。
2)局部变量的作用域,在函数定义的时候就已经确定下来了。
出于各种原因,我们有时候需要得到函数内部的局部变量。但是正常情况下这是办不到的。只有变通一下才能实现,那就是在函数内部再定义一个函数。外部变量不能访问内部变量,内部变量却能访问外部变量,这正是因为JS特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以父对象的所有变量,对子对象都是可见的,反之则不成立。我们只需要把子函数返回出来,我们就可以在外部读取内部变量了。
4 . 闭包的应用场景
1)函数作为返回值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script>
function f1() {
var n = ;
nAdd = function () {
n += ;
}
function f2() {
console.log(n)
}
return f2;
}
var result = f1();
console.log("result的第一次执行")
result();//
console.log("nAdd的执行")
nAdd();//无输出
console.log("result的第二次执行")
result();//
</script>
</body>
</html>
2)函数作为参数被传递。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包</title>
</head>
<body>
<script>
function fun(n, o) {
console.log(o);
return {
fun: function (m) {
return fun(m, n);
}
};
}
var a = fun(); //undefined
// 执行完并未销毁保存在内存中
a.fun(); //
a.fun(); //
a.fun(); // fun().fun().fun().fun();
//undefined、0、1、2
var a = fun().fun();
//undefined、0
a.fun();
//undefined、1
a.fun();
//undefined、1
</script>
</body>
</html>
5 . 使用闭包注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包。否则会造成网页性能问题,在IE中可能导致内存泄漏。解决方法就是在函数退出之前,将不使用的局部变量删除(值置为null,垃圾回收机制就会处理)。
2)闭包会在父函数外部,改变父函数内部变量的值。所以不要随便改变父函数内部变量的值。
6 . demo通过js闭包实现鼠标滑过隔行换色的效果
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>闭包实现各行换色</title>
<style type="text/css">
*{
margin: ;
padding: ;
}
h3{
text-align: center;
font-size: 24px;
line-height: 60px;
}
.newList{
width: %;
margin: auto;
list-style: none;
}
.newList li{
text-indent: 24px;
line-height: 50px;
font-size: 16px;
border-top: 1px dashed #eeeeee;
}
</style>
</head>
<body>
<h3>新闻列表</h3>
<ul id="newList" class="newList">
<li>这是第1条新闻</li>
<li>这是第2条新闻</li>
<li>这是第3条新闻</li>
<li>这是第4条新闻</li>
<li>这是第5条新闻</li>
<li>这是第6条新闻</li>
<li>这是第7条新闻</li>
<li>这是第8条新闻</li>
<li>这是第9条新闻</li>
</ul>
</body>
<script type="text/javascript">
var oNewList=document.getElementById('newList');
var oNewListArr=Array.from(oNewList.children);
oNewListArr.forEach(function (v,i) {
// 隔行换色
if(i % === ) {
oNewListArr[i].style.background = '#f3f3f3';
};
//鼠标滑过改变背景色
v.onmouseover=function () {
this.style.background="#87ceeb";
};
//鼠标滑过恢复原背景色
(function (m){
oNewListArr[m].onmouseout=function () {
if(m % === ) {
oNewListArr[i].style.background = '#f3f3f3';
}
else{
oNewListArr[i].style.background = '#ffffff';
}
}
})(i);
})
</script>
</html>
二、构造函数的继承
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new 运算符,就能生成实例,并且this变量会绑定在实例对象上。
详解new的执行过程:
- 在内存生成一个实例对象obj。
 - 指定实例对象的__proto__到构造函数的prototype。
 - 运行构造函数,相当于运行fn.call(obj)。
 - 检查返回值,如果返回值为基本数据类型,则无视该返回值,而将生成的对象返回。如果为引用类型,则将该返回值返回。
 
构造函数很好用,但是存在浪费内存的问题。
JS规定,每一个构造函数都有一个prototype属性,指向另一个对象。此对象的所有属性和方法,都会被构造函数的实例继承。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>构造函数</title>
</head>
<body>
</body>
<script type="text/javascript">
//构造函数的命名首字母一般大写
function Car(carlogo) {
this.carLogo = carlogo;
this.whistle = function () {
console.log("正在鸣笛......")
}
}
Car.prototype.run = function() {
console.log("正在行走......")
};
var bmw=new Car("BMW");
var audi=new Car("Audi");
console.log("构造函数创建的实例:")
console.log(bmw === audi)
console.log(bmw,audi)
console.log("构造函数whisle属性:")
console.log(bmw.whisle === audi.whisle)
console.log("原型run方法:");
console.log(bmw.run === audi.run); </script>
</html>

- instanceof运算符:判断对象是不是构造函数的实例,是则true,否则false。
 
console.log(bmw instanceof Car)//true
console.log(bmw instanceof Array)//false
- isPrototypeOf:判断prototype对象和某个实例之间的关系。
 
console.log(Car.prototype.isPrototypeOf(bmw));//true
console.log(Array.prototype.isPrototypeOf(bmw));//false
- hasOwnProperty:每个实例对象都有一个hasOwnProperty方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
 
    bmw.hasOwnProperty('carlogo');//true
    bmw.hasOwnProperty('run');//false
- in运算符:in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。
 
console.log('run' in bmw);//true
注:
详解instanceof 运算符。 // 会沿着原型链查找。
详解hasOwnProperty方法。 // 不会沿着原型链查找。
详解isPrototypeOf方法。 // 会沿着原型链查找。
详解in运算符。 // 会沿着原型链查找。
三、call/apply继承
call和apply都是为了改变某个函数运行时的context即上下文而存在的,即改变函数内部this的指向。
Fn.call(obj, arg1, arg2 [, argN]);
fn,.apply(obj, [arg1, arg2,…, argN]);
作用相同,apply以数组的形式传参,call是以列表的形式。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>构造函数</title>
</head>
<body>
</body>
<script type="text/javascript">
//构造函数
function Person(name) {
this.name = name;
} var xiaoJun = {
name: '小军'
}; var xiaoHua = {
name: '小花',
sing: function (where, music) {
console.log(this.name + '正在' + where + '唱' + music);
}
}; xiaoHua.sing('马路上', '葫芦娃');
//列表形式传参
xiaoHua.sing.call(xiaoJun, '音乐厅', '屋顶');
//数组方式传参
xiaoHua.sing.apply(xiaoJun, ['KTV', '我们不一样']);
//列表形式传参,且与call、apply的执行方式不同,需要调用
var func = xiaoHua.sing.bind(xiaoJun);
func('教室', '荡起双桨'); </script>
</html>
apply和call实现继承:
将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>继承</title>
</head>
<body>
</body>
<script type="text/javascript">
function Animal () {
this.eyes = ;
}
function Dog(name) {
Animal.call(this);
this.name = name;
}
var oWangCai = new Dog('旺财');
console.log(oWangCai); </script>
</html>
四、prototype的概念
- 一切引用类型都是对象。
 - 对象是属性的集合。
 - 对象都是通过构造函数创建的。
 - 每个函数都有一个prototype属性,即原型。对象都有__proto__属性,可以成为隐式原型。这个__proto__属性是一个隐藏的属性,JS并不希望开发者能够用到这个属性,有的低版本浏览器甚至不支持这个属性值。
 - 每个对象的__proto__属性指向创建该对象的函数的prototype。
 - Object.prototype.__proto__指向null。
 
原型链:
访问一个对象的属性时,先在本地属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。

constructor属性:函数的prototype有一个constructor属性,该属性指向了函数本身。对象没有constructor属性,它沿着原型链使用的是对象的构造函数的constructor属性。
五、this的使用情况
1:构造函数(在new的情况下)
this指向的是新构建出来的对象。
2:函数作为对象的一个属性
函数中的this指向的是该对象。
3:函数call或者apply
当函数被call或者apply调用时,this的值就取传入对象的值。
4:全局函数 & 普通函数
全局函数或者普通函数中,this指向的是window。
5:在prototype原型对象中,this指向的是调用构造函数实例出来的对象。
注:this关键字的值在函数运行的时候才会被指定。
六、原型链的继承:
将一个构造函数的原型指向另一个构造函数的实例对象来实现继承。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原型链继承</title>
</head>
<body>
</body>
<script type="text/javascript">
function Person() {
this.age = ;
} function Man() {
this.beard = '胡子';
} var person = new Person();
//改变man的原型指向
Man.prototype = person;
var man1 = new Man();
console.log(man1.beard);
console.log(man1.age); </script>
</html>

原型链的继承必须将Man的prototype.constructor指向更改过来,否则它将会指向People,发生原型混乱。也就是下一点讲述的混合继承
七、混合继承:
原型链与构造函数的优点,组合而成的一个模式,即原型链继承方法,而在构造函数继承属性
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>混合继承</title>
</head>
<body>
</body>
<script type="text/javascript">
function Person() {
this.age = ;
} Person.prototype.introduce = function () {
console.log('我有' + this.beard + ';今年' + this.age + '岁');
} function Man() {
Person.call(this);
this.beard = '胡子';
} var person = new Person();
console.log("prototype.constructor指向未改:")
Man.prototype = person;//改变函数原型指向
person.age = ;
var man1 = new Man();
var man2 = new Man();
console.log(man1.age);
console.log(man2.age); console.log("prototype.constructor指向改变:")
Man.prototype.constructor = Man;//改变构造函数指向
var man3 = new Man();
var man4 = new Man();
man3.age = ;
console.log(man3);
console.log(man4);
man3.introduce();
man4.introduce(); </script>
</html>

从零开始学习前端JAVASCRIPT — 12、JavaScript面向对象编程的更多相关文章
- 从零开始学习前端开发 — 12、CSS3弹性布局
		
一.分栏布局 1.设置栏数column-count:数值; 2.设置每栏的宽度column-width:数值+单位; 注:当设置了column-width,column-count会失效,二者设置其一 ...
 - 从零开始学习前端JAVASCRIPT — 11、Ajax-前后端异步交互以及Promise-异步编程的改进
		
(注:本章讲解涉及部分后端知识,将以php提供数据的方式进行相应的demo实现) 1:ajax的概念 全称:Asynchronous Javascript And Xml AJAX不是一种新的编程语言 ...
 - 从零开始学习前端JAVASCRIPT — 1、JavaScript基础
		
1:定义:javascript是一种弱类型.动态类型.解释型的脚本语言. 弱类型:类型检查不严格,偏向于容忍隐式类型转换. 强类型:类型检查严格,偏向于不容忍隐式类型转换. 动态类型:运行的时候执行类 ...
 - 从零开始学习前端JAVASCRIPT — 10、JavaScript基础ES6(ECMAScript6.0)
		
ECMAScript 6.0(简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发 ...
 - 深入理解javascript中实现面向对象编程方法
		
介绍Javascript中面向对象编程思想之前,需要对以下几个概念有了解: 1. 浅拷贝和深拷贝:程序在运行过程中使用的变量有在栈上的变量和在堆上的变量,在对象或者变量的赋值操作过程中,大多数情况先是 ...
 - javaScript设计模式之面向对象编程(object-oriented programming,OOP)(二)
		
接上一篇 面向对象编程的理解? 答:面向对象编程,就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法).这个对象我们称之为类.面向对象编程思想其中一个特点就是封装,就是把你需 ...
 - javaScript设计模式之面向对象编程(object-oriented programming,OOP)(一)
		
面试的时候,总会被问到,你对javascript面向对象的理解? 面向对象编程(object-oriented programming,OOP)是一种程序设计范型.它讲对象作为程序的设计基本单元,讲程 ...
 - JavaScript中的面向对象编程,详解原型对象及prototype,constructor,proto,内含面向对象编程详细案例(烟花案例)
		
面向对象编程: 面向:以什么为主,基于什么模式 对象:由键值对组成,可以用来描述事物,存储数据的一种数据格式 编程:使用代码解决需求 面向过程编程: 按照我们分析好的步骤,按步 ...
 - C#学习笔记(六)——面向对象编程简介
		
一.面向对象编程的含义 * 是一种模块化编程方法,使代码的重用性大大的增加. * oop技术使得项目的设计阶段需要的精力大大的增加,但是一旦对某种类型的数据表达方式达成一致,这种表达方式就可以 ...
 - python学习第十四天 -面向对象编程基础
		
python也是支持面向对象编程的.这一章节主要讲一些python面向对象编程的一些基础. 什么是面向对象的编程? 1.面向对象编程是一种程序设计范式 2.把程序看做不同对象的相互调用 3.对现实世界 ...
 
随机推荐
- PHP 构造方法 __construct()(转)
			
PHP 析构方法 __destruct() 构造方法是类中的一个特殊方法.当使用 new 操作符创建一个类的实例时,构造方法将会自动调用,其名称必须是 __construct() . 在一个类中只能声 ...
 - Struts2中拦截器的使用与配置
			
一,拦截器是什么? 拦截器是在Action执行之前和之后执行的代码,是一个类似于过滤器的类: 二,拦截器的作用 拦截器拦截Action的请求,在Action之前或之后实现某项功能: 三,拦截器的特点 ...
 - Docker之镜像
			
镜像(Images) 镜像是Docker的三大核心之一,类似于虚拟机,作用和虚拟机是一样的,唯独是组成部分会有些区别.简单的说如果我们想启动一个容器就必须要有镜像.docker运行容器前需要本地存在对 ...
 - LINUX文件定位
			
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
 - SpringMVC源码情操陶冶-DispatcherServlet简析(二)
			
承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...
 - StringBuffer和String需要注意的
			
首先,StringBuffer的toString方法和String的subString方法都是在新生成了一个新的String. 最近做的一个功能,多线程的从SQLite数据库中读取数据.将数据拼成在M ...
 - HTTP/1.1与HTTP/1.0的区别[转]
			
原文链接:http://blog.csdn.net/forgotaboutgirl/article/details/6936982 下面主要从几个不同的方面介绍HTTP/1.0与HTTP/1.1之间的 ...
 - BZOJ 4008: [HNOI2015]亚瑟王 [DP 概率 !!!]
			
传送门 题意: $r$轮$n$张卡牌,每一轮依次考虑每张卡牌,$p_i$概率发动造成$d_i$伤害后结束本轮或者继续考虑下一张 每张卡牌发动过之后以后都会跳过 求$r$轮之后的期望伤害 看了一节课出题 ...
 - 一个开源的强类型客户端(.NET 中的 Open Fegin)— Rabbit Go
			
在做RabbitCloud(之前是一个RPC,现在是一个微服务框架)的时候往往避不开客户端代理,之前把这些客户端代理都算作服务框架不可缺少的一部分,随着后期的深入发现这些客户端代理其实可以互通,类似s ...
 - 关于ES6 用箭头函数后的 this 指向问题
			
最近写完小程序后, 开始学习React, 因为有编译器, 就直接用ES6 新语法了, 中间自然离不开 () => { console.log('箭头函数的this是指向哪的问题')}; var ...