一、闭包

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的执行过程:

  1. 在内存生成一个实例对象obj。
  2. 指定实例对象的__proto__到构造函数的prototype。
  3. 运行构造函数,相当于运行fn.call(obj)。
  4. 检查返回值,如果返回值为基本数据类型,则无视该返回值,而将生成的对象返回。如果为引用类型,则将该返回值返回。

构造函数很好用,但是存在浪费内存的问题。

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的概念

  1. 一切引用类型都是对象。
  2. 对象是属性的集合。
  3. 对象都是通过构造函数创建的。
  4. 每个函数都有一个prototype属性,即原型。对象都有__proto__属性,可以成为隐式原型。这个__proto__属性是一个隐藏的属性,JS并不希望开发者能够用到这个属性,有的低版本浏览器甚至不支持这个属性值。
  5. 每个对象的__proto__属性指向创建该对象的函数的prototype。
  6. 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面向对象编程的更多相关文章

  1. 从零开始学习前端开发 — 12、CSS3弹性布局

    一.分栏布局 1.设置栏数column-count:数值; 2.设置每栏的宽度column-width:数值+单位; 注:当设置了column-width,column-count会失效,二者设置其一 ...

  2. 从零开始学习前端JAVASCRIPT — 11、Ajax-前后端异步交互以及Promise-异步编程的改进

    (注:本章讲解涉及部分后端知识,将以php提供数据的方式进行相应的demo实现) 1:ajax的概念 全称:Asynchronous Javascript And Xml AJAX不是一种新的编程语言 ...

  3. 从零开始学习前端JAVASCRIPT — 1、JavaScript基础

    1:定义:javascript是一种弱类型.动态类型.解释型的脚本语言. 弱类型:类型检查不严格,偏向于容忍隐式类型转换. 强类型:类型检查严格,偏向于不容忍隐式类型转换. 动态类型:运行的时候执行类 ...

  4. 从零开始学习前端JAVASCRIPT — 10、JavaScript基础ES6(ECMAScript6.0)

    ECMAScript 6.0(简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发 ...

  5. 深入理解javascript中实现面向对象编程方法

    介绍Javascript中面向对象编程思想之前,需要对以下几个概念有了解: 1. 浅拷贝和深拷贝:程序在运行过程中使用的变量有在栈上的变量和在堆上的变量,在对象或者变量的赋值操作过程中,大多数情况先是 ...

  6. javaScript设计模式之面向对象编程(object-oriented programming,OOP)(二)

    接上一篇 面向对象编程的理解? 答:面向对象编程,就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法).这个对象我们称之为类.面向对象编程思想其中一个特点就是封装,就是把你需 ...

  7. javaScript设计模式之面向对象编程(object-oriented programming,OOP)(一)

    面试的时候,总会被问到,你对javascript面向对象的理解? 面向对象编程(object-oriented programming,OOP)是一种程序设计范型.它讲对象作为程序的设计基本单元,讲程 ...

  8. JavaScript中的面向对象编程,详解原型对象及prototype,constructor,proto,内含面向对象编程详细案例(烟花案例)

    面向对象编程:   面向:以什么为主,基于什么模式 对象:由键值对组成,可以用来描述事物,存储数据的一种数据格式 编程:使用代码解决需求   面向过程编程:         按照我们分析好的步骤,按步 ...

  9. C#学习笔记(六)——面向对象编程简介

    一.面向对象编程的含义 *   是一种模块化编程方法,使代码的重用性大大的增加. *   oop技术使得项目的设计阶段需要的精力大大的增加,但是一旦对某种类型的数据表达方式达成一致,这种表达方式就可以 ...

  10. python学习第十四天 -面向对象编程基础

    python也是支持面向对象编程的.这一章节主要讲一些python面向对象编程的一些基础. 什么是面向对象的编程? 1.面向对象编程是一种程序设计范式 2.把程序看做不同对象的相互调用 3.对现实世界 ...

随机推荐

  1. JavaScript数据结构 (手打代码)

    array: 数组创建: ); //创建一个长度为6的数组 ,,,,,); 数组方法: var str="I love javascript"; var single=str.sp ...

  2. Zabbix-3.2.4实现微信(WeChat)告警

    摘自abcdocker网站 原文地址:https://www.abcdocker.com/abcdocker/2472 Zabbix可以通过多种方式把告警信息发送到指定人,常用的有邮件,短信报警方式, ...

  3. canvas-海底气泡(面向对象编程)

    需求:自动生成若干气泡,从海底往上浮: 1.基本的HTML结构: <!DOCTYPE html> <html> <head lang="en"> ...

  4. Flask之基于route装饰器的路由系统(源码阅读解析)

    一 路由系统 1. 在flask中配置URL和视图函数的路由时,首先需要在main.py中实例化一个app对象: from flask import Flask, render_template ap ...

  5. 不要用for循环去遍历LinkedList

    ArrayList与LinkedList的普通for循环遍历 对于大部分Java程序员朋友们来说,可能平时使用得最多的List就是ArrayList,对于ArrayList的遍历,一般用如下写法: p ...

  6. Python基础篇(二)

    Python最基本的数据结构是序列(sequence),序列中的每个元素被分以以0开头的唯一的一个id号. Python中有6种内建的序列:列表,元组,字符串,Unicode字符串,buffer对象和 ...

  7. 济南清北学堂游记 Day 7.

    完结撒花? 最后的模拟赛与讲题.大家似乎仍然乐在其中,虽然看评测结果,仍然是满地红. 其实到了最后已经是没有多少可写的了..... 一个精彩的地方是晚上填表的时候,群里炸锅了已经.到处都在刷CCL老师 ...

  8. 济南清北学堂游记 Day 1.

    快住手!这根本不是暴力! 刷了一整天的题就是了..上午三道题的画风还算挺正常,估计是第一天,给点水题做做算了.. rqy大佬AK了上午的比赛! 当时我t2暴力写挂,还以为需要用啥奇怪的算法,后来发现, ...

  9. python3加密解密模块 cryptography

    cryptography 的目标是成为"人类易于使用的密码学包cryptography for humans",就像 requests 是"人类易于使用的 HTTP 库H ...

  10. Linux 虚拟IP

    虚拟IP Linux网卡上绑定另一个虚拟ip,即网卡上一个真实ip一个虚拟ip.当然通过这2个ip都可以连接到该主机. 实现原理主要是靠TCP/IP的ARP协议.因为ip地址只是一个逻辑 地址,在以太 ...