JS04函数闭包面向对象继承贪吃蛇
js闭包的理解
js闭包的理解
javascript闭包是一个由函数和与其相关联的词法环境组合而成的特殊类型的语法结构。
结构本身无法释放外部变量,因此可以创建一个函数闭包,该函数将外部变量封装在内部,从而保护变量不被外部干扰。
闭包在javascript中的主要用途是当你想要跨域定义一些变量时,使得它们不能被更改或删除,能够阻止被周围的其它函数可能无意中(或者有意)更改它们,这些变量就可以封装在一个闭包中,这样就可以保护它们不被无意更改。
闭包也可以被用来创建私有变量和私有函数,让他们只有内部可以访问到,形成对更改的保护。
闭包还是一种用来避免变量全局污染的有效手段,因为它可以把变量绑定到指定的函数中,且只能在该函数内部访问到,而不会影响全局变量的值。
闭包也可以用来缓存数据,使数据可以被重复调用,而不需要重新构建。
另外,javascript中的闭包也可以被用来及时执行一段代码,这段代码只会在一个特定的时候被执行,而当这个特定的时候到来时,就会自动执行。
总之,javascript中的闭包是一种很重要的语法结构,它可以实现变量封装保护,私有变量和缓存数据,甚至及时执行一段代码,所以很多有趣的功能都可以用它来实现。
JS100行代码实现贪吃蛇,快写给你的女朋友
JS100⾏代码实现贪吃蛇,快写给你的⼥朋友<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>贪吃蛇</title><style>*{margin: 0;padding: 0;}#root{width: 100%;font-size: 0;}.block{display: inline-block;}</style></head><body><div id="root"></div><script>const BLOCK_LENGTH = 30; //⽅格边长const BLOCK_COLOR = "white"; //⽅格颜⾊const FOOD_COLOR = "salmon"; //⾷物颜⾊const HEAD_COLOR = "orchid"; //头部颜⾊const BODY_COLOR = "plum"; //⾝体颜⾊let js = 0; //定时器IDlet w_size = Math.floor((window.screen.width || 450) / BLOCK_LENGTH), //横向⽅格数量h_size = Math.floor((document.documentElement.clientHeight || 450) / BLOCK_LENGTH); //纵向⽅格数量//⽣成地图 startif(true){let map = "";for(let i = 0, len = w_size * h_size; i < len; ++i){map += `<div class='block' style='width:${BLOCK_LENGTH}px;height:${BLOCK_LENGTH}px;background-color:${BLOCK_COLOR};'></div>`; }document.getElementById('root').innerHTML = map;}//⽣成地图 endlet snake_list = [0]; //蛇⾝let snake_length = 1; //蛇⾝长度let block_list = document.getElementsByClassName('block'); //⽅格列表function creat_food(){ //⽣成⾷物let sub = 0;if(snake_list.length / (w_size * h_size) < 0.75){sub = Math.floor(Math.random()*(w_size*h_size));while(block_list[sub].style.backgroundColor != BLOCK_COLOR){sub = Math.floor(Math.random()*(w_size*h_size));}}else{let block_arr = [];for(let i = 0, len = w_size * h_size; i < len; ++i){if(block_list[i].style.backgroundColor == BLOCK_COLOR){block_arr.push(i);}}sub = block_arr[Math.floor(Math.random()*(block_arr.length))];}block_list[sub].style.backgroundColor = FOOD_COLOR;}let dir = 4; //移动⽅向(上:1下:2左:3右:4)function move(){ //移动let handle = function(next){let max = next > snake_list[snake_length - 1] ? next : snake_list[snake_length - 1];if(block_list[next] == undefined ||block_list[next].style.backgroundColor == BODY_COLOR ||(Math.abs(next - snake_list[snake_length - 1]) == 1 &&max % w_size == 0)){clearInterval(js);alert("得分:" + snake_length);location.reload();}else if(block_list[next].style.backgroundColor == FOOD_COLOR){block_list[snake_list[snake_length - 1]].style.backgroundColor = BODY_COLOR;snake_list.push(next);++snake_length;block_list[next].style.backgroundColor = HEAD_COLOR;creat_food();}else{block_list[snake_list[snake_length - 1]].style.backgroundColor = BODY_COLOR;block_list[snake_list[0]].style.backgroundColor = BLOCK_COLOR;snake_list.shift();snake_list.push(next);block_list[snake_list[snake_length - 1]].style.backgroundColor = HEAD_COLOR;}};switch(dir){case 1:handle(snake_list[snake_length - 1] - w_size);break;case 2:handle(snake_list[snake_length - 1] + w_size);break;case 3:handle(snake_list[snake_length - 1] - 1);break;case 4:handle(snake_list[snake_length - 1] + 1);break;default:;}}document.onkeypress = function(e){let theEvent = e || window.event;let code = theEvent.keyCode || theEvent.which || theEvent.charCode;switch(code){case 38: case 119:(dir == 1 || dir == 2) ? void 0 : dir = 1;break;case 37: case 97:(dir == 3 || dir == 4) ? void 0 : dir = 3;break;case 40: case 115:(dir == 1 || dir == 2) ? void 0 : dir = 2;break;case 39: case 100:(dir == 3 || dir == 4) ? void 0 : dir = 4;break;default:;}};block_list[snake_list[0]].style.backgroundColor = HEAD_COLOR;creat_food();js = setInterval(move, 300);</script></body></html>代码易读不⽤解释,只提⼀个事,蛇移动的时候,没必要每个部位都动,只处理头尾就可以了,这个应该也是显⽽易见的。
js闭包的理解和实例
js闭包的理解和实例JS闭包是一种实现函数式编程的有力工具,它也是JavaScript 编程中最重要的思想,它使开发者有能力在不破坏原有结构的情况下处理变量作用域,帮助他们更轻松地处理变量并且减少内存泄漏的可能性。
它也增加了函数的能力,可以将其作为参数传递给另一个函数。
本文主要阐述JS闭包的理解以及一些常见的实例。
首先,让我们来看看什么是JS闭包。
JS闭包是一个函数以及其相关环境的一个组合,它在函数的作用域链中存在。
当这个函数被调用时,函数内部的定义和环境将会被持久保存,直到函数被销毁为止,这种机制可以在函数调用后继续访问和修改这些数据,这使得JS闭包可以模拟私有变量,因为它们不能被外部访问到。
闭包的基本示例是这样的:function foo(){tvar b = 1;tfunction bar(){ttreturn b * 2;t}treturn bar;}var a = foo();var c = a();alert(c); // 2在这个例子中,函数foo()定义了一个私有变量b,该变量无法在外部访问,因为它是foo()函数内部定义的。
foo()返回一个函数bar,该函数可以访问foo()函数中定义的变量b,因此返回2。
另外,JS闭包也有另一个很重要的特性:它可以帮助防止变量和方法被其他程序修改。
考虑下面的代码片段:var sayMyName = (function() {tvar myName =John”;treturn function() {ttalert(myName);t}})();sayMyName(); //John”sayMyName = function(){ alert(“Bob”); }sayMyName(); //John在这个例子中,我们使用闭包保存myName变量,因此即使我们更改sayMyName函数,也不会影响myName变量。
此外,JS闭包还可以用来模拟类。
JavaScript继承详解
JavaScript继承详解∙创建一个空白对象(new Object())。
∙拷贝Person.prototype中的属性(键值对)到这个空对象中(我们前面提到,内部实现时不是拷贝而是一个隐藏的链接)。
∙将这个对象通过this关键字传递到构造函数中并执行构造函数。
∙将这个对象赋值给变量zhang。
这种在JavaScript内部实现的隐藏的prototype链接,是JavaScript赖以生存的温润土壤,也是模拟实现继承的基础。
∙在创建Employee构造函数和原型(以后简称类)时,就对Person进行了实例化,这是不合适的。
∙Employee的构造函数没法调用父类Person的构造函数,导致在Employee构造函数中对name和sex属性的重复赋值。
∙Employee中的函数会覆盖Person中的同名函数,没有重载的机制(和上一条是一个类型的问题)。
∙创建JavaScript类的语法过于零散,不如C#/Java中的语法优雅。
∙实现中有constructor属性的指向错误,这个会在第二篇文章中讨论。
我们会在第三章完善这个例子。
JavaScript继承的实现正因为JavaScript本身没有完整的类和继承的实现,并且我们也看到通过手工实现的方式存在很多问题,因此对于这个富有挑战性的任务网上已经有很多实现了:这个系列的文章将会逐一深入分析这些实现,最终达到对JavaScript中如何实现类和继承有一个深入的了解。
下一章我们将会介绍在类实现中的相关知识,比如this、constructor、prototype等。
JavaScript继承详解(二)这一章我们将会重点介绍JavaScript中几个重要的属性(this、constructor、prototype),这些属性对于我们理解如何实现Java Script中的类和继承起着至关重要的作用。
thisthis表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window;如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。
js比较难理解的知识点 知乎
js比较难理解的知识点知乎以js比较难理解的知识点JavaScript作为一门非常灵活和强大的编程语言,有一些概念和知识点对于初学者来说可能会比较难以理解。
本文将介绍一些常见的js知识点,帮助读者更好地理解和掌握这门语言。
1. 闭包(Closures)闭包是JavaScript中非常重要但也容易令人迷惑的概念之一。
简单来说,闭包是指一个函数能够访问并操作其外部函数的变量,即使外部函数已经执行完毕。
这是由于JavaScript的作用域链机制,函数在创建时会保存对其外部作用域的引用。
闭包的使用可以带来很多好处,但也容易导致内存泄漏和性能问题,因此需要谨慎使用。
2. 原型链(Prototype chain)原型链是JavaScript实现继承的一种机制。
每个对象都有一个原型对象,而原型对象又有自己的原型对象,形成一个链式结构。
当访问一个对象的属性或方法时,如果该对象自身没有定义,则会沿着原型链向上查找。
这种机制可以实现属性和方法的继承,但也容易导致混乱和不可预测的行为。
3. 异步编程(Asynchronous programming)JavaScript是一门单线程的语言,但通过异步编程可以实现非阻塞的操作,提高程序的性能和响应能力。
异步编程的方式有很多,包括回调函数、Promise、Generator和async/await等。
这些概念和技术需要理解其工作原理和使用方式,以便编写高效的异步代码。
4. 作用域(Scope)作用域是指变量和函数的可访问范围。
JavaScript采用的是词法作用域,即变量的作用域在代码编写时就确定了。
在函数内部可以访问函数外部的变量,但函数外部不能访问函数内部的变量。
此外,JavaScript还有全局作用域和块级作用域等概念,需要理解其作用和使用方式。
5. this关键字this关键字是一个非常复杂和容易引起困惑的概念。
它的值取决于函数的调用方式,而不是函数的定义位置。
在全局作用域中,this 指向全局对象(浏览器中是window对象),而在函数内部,this 的值可能会有所不同。
js闭包的原理与应用
JavaScript闭包的原理与应用什么是闭包闭包是JavaScript中一种非常重要的概念,能够有效地管理和保护变量,同时也是实现一些高级编程技巧的关键。
简单来说,闭包是由函数以及定义在函数内部的函数所组成的,内部函数可以访问外部函数的变量和参数,即使外部函数已经执行结束。
闭包的原理在JavaScript中,每当一个函数被创建时,都会同时创建一个作用域链。
作用域链是一种关系链,它由函数的作用域和上层函数的作用域组成。
当内部函数需要访问外部函数的变量时,它会从自己的作用域链中依次查找,直到找到该变量为止。
这就是闭包的原理。
闭包的应用闭包在JavaScript中有许多应用场景,下面列举了几个常见的应用:1.保护变量:使用闭包可以创建私有变量,防止外部代码直接修改变量的值。
这在封装一些敏感信息或者保护一些关键变量时非常有用。
2.延长变量的生命周期:闭包可以使得一个变量的生命周期延长,即使外部函数已经执行完毕。
这在一些异步操作中常常用到,例如定时器、事件监听等。
3.缓存数据:由于闭包可以访问外部函数的变量,可以将一些中间结果缓存起来,以提高代码的性能和效率。
4.模块化开发:通过闭包,可以将代码模块化,避免全局变量的污染,提高代码的可维护性和可重用性。
使用闭包的注意事项虽然闭包在JavaScript中有着广泛的应用,但是在使用闭包时也需要注意一些问题。
1.内存泄漏:由于闭包使得变量的生命周期延长,如果不小心使用了过多的闭包,可能会导致内存泄漏的问题。
因此,在使用闭包时需要注意及时释放内存。
2.性能问题:闭包会带来额外的开销,因为每个闭包都会创建一个新的作用域链。
因此,在性能要求较高的场景下,需要慎重使用闭包。
3.变量共享:在使用闭包时,需要注意变量的访问权限。
闭包可以访问外部函数的变量,但是外部函数不能访问闭包中的变量。
总结闭包是JavaScript中非常重要的概念,它可以有效地管理和保护变量,实现一些高级编程技巧。
贪吃蛇(HTML小游戏使用JavaScript开发)
贪吃蛇(HTML小游戏使用JavaScript开发)贪吃蛇:HTML小游戏使用JavaScript开发在游戏界,贪吃蛇是非常经典和受欢迎的一款小游戏。
它的简单和上瘾性使得无数玩家沉迷其中。
今天,我们将学习如何使用HTML和JavaScript来开发一个贪吃蛇的小游戏。
一、游戏的基本思路贪吃蛇的游戏规则非常简单明了。
玩家控制蛇的移动,通过吃食物来不断增长蛇的长度。
当蛇碰到墙壁或者自己的身体时,游戏结束。
游戏的目标是使蛇长得尽可能长,挑战自己的最高得分。
二、HTML布局首先,我们需要在HTML文件中创建游戏画布。
这个画布将用于显示游戏的界面。
我们可以通过HTML的"canvas"元素来实现。
```html<!DOCTYPE html><html><head><title>贪吃蛇</title><style>#gameCanvas {border: 1px solid black;}</style></head><body><canvas id="gameCanvas" width="400" height="400"></canvas><script>// 在这里编写JavaScript代码</script></body></html>```上面的代码中,我们创建了一个宽高为400像素的画布,并给它设置了一个边框。
三、JavaScript逻辑接下来,我们需要使用JavaScript来实现游戏的逻辑。
我们将使用一个JavaScript类来表示贪吃蛇,并在其中实现移动、吃食物等功能。
```javascript<script>class SnakeGame {constructor(canvasId) {this.canvas = document.getElementById(canvasId);this.context = this.canvas.getContext("2d");this.snake = new Snake();this.food = new Food();// 在这里添加事件监听器,监听用户的方向键输入this.gameLoop();}// 游戏主循环gameLoop() {// 清空画布this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); // 更新蛇的位置this.snake.update();// 绘制蛇和食物this.snake.draw(this.context);this.food.draw(this.context);// 在下一帧时再次调用游戏主循环requestAnimationFrame(() => this.gameLoop()); }}class Snake {constructor() {// 在这里初始化蛇的位置和长度等信息}update() {// 在这里更新蛇的位置和长度等信息}draw(context) {// 在这里使用context绘制蛇的形状}}class Food {constructor() {// 在这里初始化食物的位置等信息}draw(context) {// 在这里使用context绘制食物的形状}}// 创建一个名为"game"的SnakeGame实例const game = new SnakeGame("gameCanvas");</script>```在上面的代码中,我们创建了一个`SnakeGame`类来表示游戏,`Snake`类来表示蛇,和`Food`类来表示食物。
js闭包的理解和实例
js闭包的理解和实例JS闭包是JavaScript的一个重要的特性,它的理解和应用对于JS开发人员来说是非常重要的。
本文将介绍JS闭包的定义,特性,优势及其实例。
JS闭包是一种函数定义,它可以让一个函数访问外部作用域中的变量,即使外部作用域在函数定义完成后也是如此。
就是说,在函数体内定义的变量可以在函数外部使用,在函数外部定义的变量也可以在函数体内使用,这叫做闭包。
一个JS闭包由两个部分组成,即函数定义和环境变量,它可以使函数访问外部环境中定义的任何变量。
当函数被外部环境调用时,它们就会记住这些变量的值,这实际上就是JS闭包的实现原理。
JS闭包有很多优点,首先它可以让函数访问外部环境中定义的变量,而不必担心它会被重新定义;其次,它可以在一个函数体内定义另外一个函数,这样就能使函数之间的调用更加安全、简单和可靠。
此外,JS闭包还可以提供模块化的代码,也就是一个函数可以用来实现另外一个函数的功能。
下面我们来看一个JS闭包的例子:function f1(){var x = 5;return function f2(){return x++;}}var func= f1(); //调用函数f1()console.log(func()); // 5console.log(func()); // 6在上面的例子中,函数 f2()定义在函数 f1()作用域中,并且函数f2()可以访问外部函数f1()中定义的变量 x。
当函数 f1()调用时,数 f2形成了闭包,此时它可以访问外部环境中定义的变量,也就是变量 x值。
从上面的例子可以看出,包可以使函数体内的变量不受外部环境影响,从而实现模块化的代码。
当程序员使用JS做开发的时候,牢记JS的闭包特性,就能让我们利用闭包实现模块化的编程,有助于代码的复用、重构和维护,从而提高代码的健壮性和扩展性。
综上所述,JS闭包是一种函数定义,它可以让一个函数访问外部作用域中的变量,即使外部作用域在函数定义完成后也是如此。
js高级知识点总结
js高级知识点总结1. 闭包(Closures)闭包是JavaScript中一个非常强大的特性,它允许内部函数访问外部函数的作用域。
闭包可以让我们创建私有变量、模拟私有方法,并且可以创建可重用的函数。
下面是一个闭包的示例:```javascriptfunction outerFunction() {var outerVariable = 'I am outer';function innerFunction() {console.log(outerVariable);}return innerFunction;}var innerFunc = outerFunction();innerFunc(); // 输出:I am outer```在这个示例中,innerFunction可以访问outerFunction的作用域中的outerVariable变量,这就是闭包的特性。
闭包能够帮助我们编写更加模块化和封装的代码。
2. 原型链(Prototype Chain)JavaScript是一种基于原型的语言,所有的对象都有一个原型对象。
原型对象又可以拥有自己的原型对象,这样就形成了原型链。
当我们访问一个对象的属性或方法时,JavaScript会在原型链中进行查找,直到找到匹配的属性或方法为止。
```javascriptfunction Animal(name) { = name;}Animal.prototype.sayHello = function() {console.log(`Hello, my name is ${}`);}function Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;var myDog = new Dog('Buddy', 'Golden Retriever');myDog.sayHello(); // 输出:Hello, my name is Buddy```在这个示例中,我们创建了一个Animal类型的对象,它有一个原型对象,这个原型对象上有sayHello方法。
javascript实现贪吃蛇小游戏思路
javascript实现贪吃蛇⼩游戏思路javascript⼩游戏贪吃蛇实现思路讲解(完整代码实现),供⼤家参考,具体内容如下效果流程1、⾸先我们要操作的canvas<!doctype html><html><head><meta charset="utf-8"><title>贪吃蛇</title></head><body><canvas id="canvas"></canvas> <!-- 我们要操作的canvas --><input type="button" value="开始游戏" /><!-- 开始游戏按钮 --><script>//获取元素var canvas = document.getElementById("canvas"); //找到我们要操作的canvasvar context = canvas.getContext("2d"); //规定在canvas上操作的环境为2dvar but = document.getElementsByTagName("input")[0]; //找到开始按钮</script>2、在初始化canvas.width = 500; //定义canvas宽度canvas.height = 500; //定义canvas⾼度canvas.style.border = "5px solid #000"; //定义canvas边框var times = 100; //默认时间200毫秒var long = 10; //蛇⾝相对于步长的个数var x = y =8; //蛇的初始坐标var direction = 3; // 1 上 2 右 3 下 0 左var size = 8; //蛇每次移动的长度(步长)var map = []; //⽤来记录蛇每次移动的坐标var foodx = 0; //⾷物的初始X轴坐标var foody = 0; //⾷物的初始y轴坐标var onOff = true;var foodT = true;var timer = null;3、根据⽅向控制蛇的坐标变化,判断蛇的坐标是否超出canvas边界,判断蛇有没有碰到⾃⼰//根据⽅向控制蛇的坐标变化switch(direction){case 1: y = y - size; break; //上case 2: x = x + size; break; //右case 3: y = y + size; break; //下case 0: x = x - size; break; //左}//判断蛇的坐标是否超出canvas边界,超出则出现碰壁提⽰,游戏结束if(x>500 || x<0 || y>500 || y<0){// alert("你碰壁挂了!继续努⼒吧……");window.location.reload();}//判断蛇有没有碰到⾃⼰,碰到⾃⼰游戏结束for(var i=0; i<map.length; i++){if(parseInt( map[i].x ) == x && parseInt( map[i].y ) == y){// alert("你碰到⾃⼰挂了!继续努⼒吧……");window.location.reload(); //重新载⼊}}4、然后绘制蛇//绘制蛇if(map.length>long){var cl = map.shift();context.clearRect(cl['x'],cl['y'],size,size);}map.push({'x':x,'y':y});context.fillStyle = "#777"; //填充蛇的颜⾊context.fillRect(x,y,size,size); //绘制蛇5、判断⾷物坐标等于蛇的坐标时(蛇吃掉⾷物)//判断⾷物坐标等于蛇的坐标时(蛇吃掉⾷物)if(foodx*8 == x && foody*8 == y ){food(); //再次绘制⾷物long++; //蛇的长度增加times = times - 10; //速度加快clearInterval(timer);onOff = true;setTimeout(goto,times); //开始新的⼀轮}6、确定⾷物随机显⽰坐标,绘制⾷物//确定⾷物随机显⽰坐标,绘制⾷物function food(){foodx = Math.ceil(Math.random()*40); //⾷物的X轴随机坐标foody = Math.ceil(Math.random()*40); //⾷物的Y轴随机坐标context.fillStyle = "mediumvioletred"; //⾷物的填充颜⾊context.fillRect(foodx*8,foody*8,8,8); //绘制⾷物}7、监听按下⽅向键,获得蛇前进的⽅向//监听按下⽅向键,获得蛇前进的⽅向document.onkeydown = function(ev){var ev = ev || event;var cod = ev.keyCode - 37;switch(cod){case 1: direction = 1; break; //向上case 2: direction = 2; break; //向右case 3: direction = 3; break; //向下case 0: direction = 0; break; //向左}}完整代码实现<!doctype html><html><head><meta charset="utf-8"><title>贪吃蛇</title></head><body><canvas id="canvas"></canvas> <!-- 我们要操作的canvas --><input type="button" value="开始游戏" /><!-- 开始游戏按钮 --><script>//获取元素var canvas = document.getElementById("canvas"); //找到我们要操作的canvas var context = canvas.getContext("2d"); //规定在canvas上操作的环境为2dvar but = document.getElementsByTagName("input")[0]; //找到开始按钮//初始化canvas.width = 500; //定义canvas宽度canvas.height = 500; //定义canvas⾼度canvas.style.border = "5px solid #000"; //定义canvas边框var times = 100; //默认时间200毫秒var long = 10; //蛇⾝相对于步长的个数var x = y =8; //蛇的初始坐标var direction = 3; // 1 上 2 右 3 下 0 左var size = 8; //蛇每次移动的长度(步长)var map = []; //⽤来记录蛇每次移动的坐标var foodx = 0; //⾷物的初始X轴坐标var foody = 0; //⾷物的初始y轴坐标var onOff = true;var foodT = true;var timer = null;//根据⽅向控制蛇的坐标变化switch(direction){case 1: y = y - size; break; //上case 2: x = x + size; break; //右case 3: y = y + size; break; //下case 0: x = x - size; break; //左}//判断蛇的坐标是否超出canvas边界,超出则出现碰壁提⽰,游戏结束 if(x>500 || x<0 || y>500 || y<0){// alert("你碰壁挂了!继续努⼒吧……");window.location.reload();}//判断蛇有没有碰到⾃⼰,碰到⾃⼰游戏结束for(var i=0; i<map.length; i++){if(parseInt( map[i].x ) == x && parseInt( map[i].y ) == y){// alert("你碰到⾃⼰挂了!继续努⼒吧……");window.location.reload(); //重新载⼊}}//绘制蛇if(map.length>long){var cl = map.shift();context.clearRect(cl['x'],cl['y'],size,size);}map.push({'x':x,'y':y});context.fillStyle = "#777"; //填充蛇的颜⾊context.fillRect(x,y,size,size); //绘制蛇//判断⾷物坐标等于蛇的坐标时(蛇吃掉⾷物)if(foodx*8 == x && foody*8 == y ){food(); //再次绘制⾷物long++; //蛇的长度增加times = times - 10; //速度加快clearInterval(timer);onOff = true;setTimeout(goto,times); //开始新的⼀轮}}//确定⾷物随机显⽰坐标,绘制⾷物function food(){foodx = Math.ceil(Math.random()*40); //⾷物的X轴随机坐标foody = Math.ceil(Math.random()*40); //⾷物的Y轴随机坐标context.fillStyle = "mediumvioletred"; //⾷物的填充颜⾊context.fillRect(foodx*8,foody*8,8,8); //绘制⾷物}//开始游戏function goto(){if(onOff){timer = setInterval(star,times);onOff = false;}if(foodT){food();foodT = false;}}//点击按钮开始游戏开始but.onclick = goto;//点击按钮,开始游戏//监听按下⽅向键,获得蛇前进的⽅向document.onkeydown = function(ev){var ev = ev || event;var cod = ev.keyCode - 37;case 1: direction = 1; break; //向上case 2: direction = 2; break; //向右case 3: direction = 3; break; //向下case 0: direction = 0; break; //向左}}</script></body></html>以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
js自调用函数和闭包
js自调用函数和闭包JavaScript中的自调用函数和闭包是两个非常重要的概念。
自调用函数是指在定义后立即调用的函数,而闭包是指函数可以访问其定义时的词法环境。
这两个概念经常一起使用,可以用来创建私有变量和方法,模拟块级作用域,并解决一些作用域相关的问题。
首先,我们来看一下自调用函数。
自调用函数使用的是立即执行函数表达式(IIFE)的形式,其定义后立即被调用执行。
IIFE的语法形式为:(function( { /* code */ })(,其中的代码会在定义后立即执行。
使用自调用函数的一个常见场景是为全局作用域添加一个命名空间,以避免变量污染。
通过将所有的代码包装在自调用函数中,可以创建一个独立的作用域,其中的变量和函数只在该函数内部可见。
这样,在全局作用域中定义的变量不会与自调用函数内部的变量产生冲突。
例如,下面的代码使用自调用函数来创建一个名为"MyApp"的命名空间:```(functiovar message = "Hello, world!";function sayHellconsole.log(message);}//其他代码...sayHello(; // 输出 "Hello, world!"})(;```上述代码中,message变量和sayHello函数都是在自调用函数内部定义的,因此在全局作用域中是不可见的。
这样可以避免与其他代码中的变量冲突。
接下来,我们来介绍闭包。
闭包是指函数可以访问其定义时的词法环境。
简单来说,闭包可以让函数访问定义时的作用域,即使函数在定义后被调用执行。
使用闭包可以创建私有变量和方法。
在JavaScript中,没有专门的私有变量和方法的语法,但通过使用闭包,可以在函数内部创建变量和方法,并将其封装在闭包中,使其只能在函数内部访问。
这样可以实现类似面向对象编程中的私有属性和方法的效果。
下面是一个使用闭包创建私有变量和方法的例子:```function countevar count = 0;function incremencount++;}function decremencount--;}function getCounreturn count;}returnincrement: increment,decrement: decrement,getCount: getCount};var counter1 = counter(;counter1.increment(;console.log(counter1.getCount(); // 输出 1```上述代码中,counter函数定义了一个私有变量count和三个闭包函数increment、decrement和getCount。
一文了解JavaScript闭包函数
⼀⽂了解JavaScript闭包函数⽬录变量作⽤域闭包的概念闭包的⽤途闭包的缺点最后总结⼀下闭包的好处与坏处总结变量作⽤域要理解JavaScript闭包,就要先理解JavaScript的变量作⽤域。
变量的作⽤域有两种:全局的和局部的(全局变量和局部变量)JavaScript中,在函数内部可以直接读取到全局变量。
var n=10function fn(){alert(n)}fn() //10⽽在函数外部⽆法读取到函数内部的变量。
function fn(){var n=10;}fn()alert(n) //n is not defined 函数外部⽆法读取到函数内部的n注意:函数内部使⽤var声明变量的时候,这个变量是局部变量,如果不使⽤var,那么这个变量就是⼀个全局变量。
例如:function fn(){n=10;}fn()alert(n) //10另外,函数的参数也是局部性的,只在函数内部起作⽤。
在正常情况下,我们是⽆法得到函数内部的局部变量的,只有变通⽅法才可以——在函数内部再声明⼀个函数。
function f1(){var n=10;function f2(){alert(n)}}f2函数可以得到f1函数内的所有局部变量,但是f1函数却⽆法得到f2函数内部的局部变量——JavaScript语⾔特有的“链式作⽤域”结构。
(即⼦对象会⼀级⼀级地向上寻找所有⽗对象的变量),所以,⽗对象的所有变量,对于⼦对象都是可见的。
f2函数可以获取到⽗级函数f1的局部变量,那么如果把f2()函数返回,在函数f1外部就可以访问到f1()函数内部的变量了。
例如:function f1(){var n=10;function f2(){alert(n)}return f2()}f1() //页⾯弹出10例⼦中的f2()函数就是⼀个闭包函数。
闭包的概念由于作⽤域原因,我们⽆法在函数外访问函数⾥⾯定义的变量,但有事我们⼜有这个需求,因此就出现了闭包的概念。
js闭包的理解
js闭包的理解js闭包是js中的一个重要的概念,它主要结合函数和作用域的概念,具有特殊的用途。
本文将介绍js闭包,并解释它是如何工作的,如何正确使用它以及它有什么优点和缺点。
首先,闭包是指一个函数引用函数内部变量(即闭合变量),这个变量可以在函数外部调用。
为了便于理解,我们可以将闭包看作一个函数中的变量。
这些变量存在于函数内部,但是可以被外部函数访问到。
需要使用闭包的时候,首先需要了解它的高级特性。
随着js的发展,函数越来越复杂,它们越来越复杂,例如可以嵌套,可以创建新的函数,还可以调用其他函数。
这些特性可以实现一些功能,例如模块化,单例模式等。
模块化就是将大型复杂程序分割成一些模块,每个模块都有自己的一些变量和函数,让程序变得更加可维护。
而实现模块化所需要的就是js闭包。
在一个函数内部,可以通过闭包创建一个新的函数,并在函数内部调用另一个函数。
闭包就是在一个函数内部创建一个新的函数,并可以实现一些特定的功能,拥有更多的变量和函数,使得程序更加模块化,更加有效率且可维护。
使用js闭包的好处是,它可以帮助我们避免局部变量污染。
js 中的局部变量会在函数运行后被销毁,但是使用闭包,可以将局部变量保留到函数外,从而让外部函数可以访问到内部函数的变量。
另外,使用闭包还可以使得程序可以更好的将数据隐藏起来,也就是封装的概念。
闭包的作用可以让数据和方法封装在一起,外部无法访问,这样可以保证数据的安全性。
最后,js闭包是一个很有用的概念,它可以实现许多的功能,例如模块化,单例模式等。
正确使用js闭包可以使程序更加模块化,更加高效,也能减少一定的变量污染。
然而,使用js闭包时也要注意一些问题,例如,当延迟加载函数时,闭包内部的变量可能会被重写。
本文介绍了js闭包的基本概念,它的特性及它的优点和缺点,希望能帮助读者更加了解js闭包,从而正确使用它来实现模块化,单例模式等。
Javascript面向对象特性实现(封装、继承、接口)
Javascript面向对象特性实现(封装、继承、接口)Javascript作为弱类型语言,和Java、php等服务端脚本语言相比,拥有极强的灵活性。
对于小型的web需求,在编写javascript时,可以选择面向过程的方式编程,显得高效;但在实际工作中,遇到的项目需求和框架较大的情况下,选择面向对象的方式编程显得尤其重要,Javascript原生语法中没有提供表述面向对象语言特性的关键字和语法(如extends、implement)。
为了实现这些面向对象的特性,需要额外编写一些代码,如下。
在开始使用OO特性之前,还需要考虑使用接口、继承所带来的弊端。
封装、接口、继承都将使代码结构变得复杂,对于编码新手有较高的要求,对于别人接受你的项目成本也提高了,在团队协作中需要根据具体需求斟酌,不要为了秀技术而写代码;同时,封装和接口都将带来一些额外的内存开销,有些可以忽略不计,有些则是得不偿失,需要注意。
一、封装var book = (function(){var COUNT = 0;//静态私有方法function checkISBN(isbn){return true;}//构造器var ctor = function(id,name,isbn){var _id,_name,_isbnthis.setId= function(id){_id=id;}this.getId = function(){return _id;}this.setName = function(name){_name = name;}this.getName = function(){return name;}this.setIsbn = function(isbn){_isbn = isbn;}this.getIsbn = function(){return isbn;}if(checkISBN){COUNT++;}this.setName(name);this.setId(id);this.setIsbn(isbn);}ctor.getCount = function(){return COUNT;}return ctor;})();//静态、共用方法book.buyCount = function(count){return count;}book.prototype = {display:function(){}}var b = new book();b.getCount();b.display();二、接口/*** 接口:实现多个类的共同性。
javascript闭包概念简单解析(推荐)
javascript闭包概念简单解析(推荐)
关于"闭包"这个概念的⽂章在⽹上铺天盖地,基本已经稀烂了,但是有时候总感觉读了这么多的⽂章还是云⼭雾罩,当然是由于它本⾝就⽐较难于理解和涉及的知识较多,还有⼀个很重要的原因就是⽹上很多教程介绍可能存在⼀定的误区,或者说侧重点不同,下⾯就通过代码实例简单的介绍⼀下什么是闭包。
代码实例⼀:
function a(){
var webName="";
console.log(webName);
}
a()
以上是⼀段⾮常简单的代码,当函数执⾏结束之后,它就会从内存中释放,⾥⾯的声明的局部变量也将在内存中被释放,⾃然就⽆法被访问,在⽹上很多例⼦中,⼤多涉及到了作⽤域问题,其实⼀旦涉及作⽤域问题就和“闭包”概念就离题了,尽管作⽤域和"闭包"存在⼀定的关系。
再来看⼀段代码:
function a(){
var webName="";
function show() {
console.log(webName);
}
return show;
}
var func = a();
func();
以上代码就形成了⼀个典型的闭包,函数a()执⾏之后,在它内部声明的变量webName依然可以使⽤。
说了这么多,闭包到底是什么,下⾯做⼀下总结:
闭包是⼀个概念,它描述了函数执⾏完毕内存释放后,依然内存驻留的⼀个现象,只要把握这个核⼼概念,闭包就不难理解了。
以上这篇javascript闭包概念简单解析(推荐)就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
javascript 对象继承原理
javascript 对象继承原理JavaScript中的对象继承原理在JavaScript中,对象继承是一种重要的特性,它允许我们创建新的对象,这些新对象可以继承父对象的属性和方法。
对象继承的原理是通过原型链来实现的。
原型链是JavaScript中实现继承的一种机制,它是一系列对象的链式结构,每个对象都有一个指向它的原型对象的引用。
当我们访问一个对象的属性或方法时,如果该对象自身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到该属性或方法为止。
让我们来看一个简单的例子来理解对象继承的原理。
假设我们有一个名为Animal的父对象,它有一个属性name和一个方法speak。
我们可以通过定义一个构造函数来创建Animal对象,并在其原型上定义属性和方法。
```javascriptfunction Animal(name) { = name;}Animal.prototype.speak = function() {console.log('Hello, my name is ' + );};var animal = new Animal('Tom');animal.speak(); // 输出:Hello, my name is Tom```在上面的例子中,我们通过构造函数Animal创建了一个Animal对象,并给它传递了一个name参数。
然后,我们在Animal的原型上定义了一个speak方法。
通过调用animal对象的speak方法,我们可以看到它输出了一段字符串。
现在,假设我们想创建一个新的对象,它继承自Animal,并且有自己的属性和方法。
我们可以通过定义一个新的构造函数来实现这个目标,并将Animal对象作为原型。
```javascriptfunction Dog(name, breed) {Animal.call(this, name);this.breed = breed;}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;Dog.prototype.bark = function() {console.log('Woof!');};var dog = new Dog('Max', 'Labrador');dog.speak(); // 输出: Hello, my name is Maxdog.bark(); // 输出: Woof!```在上面的例子中,我们定义了一个新的构造函数Dog,并通过调用Animal构造函数来初始化name属性。
js 面向对象继承的写法
js 面向对象继承的写法标题:JavaScript中的面向对象继承 - 从理论到实践内容:在JavaScript中,面向对象编程是非常重要且不可避免的一个话题。
而在面向对象编程中,继承则是一种非常常见的实现方式。
本文将从理论到实践的角度,带领大家探讨JavaScript中的面向对象继承。
1.理论基础:JavaScript中的继承方式有哪些?在JavaScript中,实现继承一般有以下几种方式:- 原型继承- 构造函数继承- 组合继承- 寄生组合式继承以上四种方式是常用的实现继承的方式,每种方式各有优缺点。
在实践中,可以根据具体情况来选用适合的继承方式。
2.实践篇:用JavaScript代码实现继承以下是一个简单的继承示例,模拟了一个动物和猫咪的继承过程。
代码如下:```// 父类function Animal(name, age) { = name;this.age = age;}// 子类function Cat(name, age, color) {Animal.call(this, name, age);this.color = color;}// 原型继承Cat.prototype = Object.create(Animal.prototype); Cat.prototype.constructor = Cat;// 测试const cat = new Cat('Tom', 3, 'white'); console.log(); // Tomconsole.log(cat.age); // 3console.log(cat.color); // white```以上代码中,我们定义了一个父类Animal和一个子类Cat,Cat 继承自Animal。
在继承的过程中,我们使用了构造函数继承和原型继承相结合的方式,即组合继承。
3.实践体验:继承的利弊分析继承作为一种面向对象编程的重要特性,它有自身的利弊。
JS闭包函数的概念及函数的继承
JS闭包函数的概念及函数的继承⼀、闭包和函数 (⼀)什么是闭包函数 概念:简单说就是函数中嵌套函数,嵌套在这⾥⾯的函数叫做闭包函数,外⾯的函数叫做闭包环境 作⽤:通过闭包函数,可以访问到闭包函数所在局部作⽤域中的变量及参数 特点:闭包的特点或本质:将局部变量常驻内存,这既是它的优点也是它的缺点,使⽤不当还会造成内存泄漏 闭包的原理:闭包是利⽤JS中的垃圾回收机制,当⼀个函数被调⽤时,会开辟空间,函数调⽤结束,会释放空间,但如果被释放空间时,发现其中有变量正在被其他函数使⽤时,则这个变量会常驻内存⽽不被回收⼆、继承和函数 (⼀)构造函数继承 1、Call/apply 继承 2、Call 与 apply 的异同: 1)第⼀个参数 this 都⼀样,指当前对象 2)第⼆个参数不⼀样:call 的是⼀个个的参数列表;apply 的是⼀个数组(arguments 也可以) 3、构造继承特点: 1)⼦类实例共享⽗类引⽤属性的问题 2)创建⼦类实例时,可以向⽗类传递参数 3)可以实现多继承(call 或 apply 多个⽗类对象) 4、构造继承缺点: 1)实例并不是⽗类的实例,只是⼦类的实例 2)只能继承⽗类的实例属性和⽅法,不能继承原型属性和⽅法 3)⽆法实现函数复⽤,每个⼦类都有⽗类实例函数的副本,影响性能 (⼆)Prototype 的概念 1、 Javascript 中的每个函数都有 prototype 属性,指向函数的原型对象。
每⼀个对象都有_ _ proto__属性,指向该对象的⽗对象。
(三)原型链继承 1、特点: 1)⾮常纯粹的继承关系,实例是⼦类的实例,也是⽗类的实例 2)⽗类新增原型⽅法/原型属性,⼦类都能访问到 3)简单,易于实现 2、缺点: 1)要想为⼦类新增原型属性和⽅法,必须要在new ⽗类()这样的语句之后执⾏。
要想为⼦类新增实例属性和⽅法,必须要在构造函数中添加。
2)⽆法实现多继承 3)来⾃原型对象的所有属性被所有实例共享 (四)混合继承 1、特点: 1)可以继承实例属性和⽅法,也可以继承原型属性和⽅法 2)既是⼦类的实例,也是⽗类的实例 3)不存在引⽤属性的共享问题 4)可传参 5)函数可复⽤ 2、缺点:调⽤了两次⽗类构造函数,⽣成了两份实例 (五)扩展:寄⽣组合继承 1、特点:完美。
JS继承与闭包及JS实现继承的三种方式
JS继承与闭包及JS实现继承的三种⽅式前⾔在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解、成员属性、静态属性、原型属性与JS原型链)。
今天我们来继续探讨剩余的内容吧。
我们都知道,⾯向对象的三⼤特征——封装、继承、多态。
封装⽆⾮就是属性和⽅法的私有化,所以我们JS中提供了私有属性和私有⽅法。
⽽JS中并没有多态,因此我们说JS是⼀门基于对象的语⾔,⽽⾮⾯向对象的语⾔。
那么,⾯向对象三⼤特征中,在JS中最重要的就是继承了。
⼀、继承的基本概念使⽤⼀个⼦类继承另⼀个⽗类,⼦类可以⾃动拥有⽗类的属性和⽅法。
>>>继承的两⽅,发⽣在两个类之间。
所以,所谓的继承,⽆⾮就是让⼦类,拥有⽗类的所有属性和⽅法。
那么,在JS中,我们要模拟实现这⼀步,有三种常⽤的⽅法可以实现。
分别是:扩展Object的prototype实现继承、使⽤call和apply实现继承、使⽤原型实现继承。
⼆、扩展Object的prototype实现继承扩展Object实现继承的本质,是我们⾃⼰写了⼀个⽅法,将⽗类的所有属性和⽅法通过遍历循环,逐个复制给⼦类。
详细步骤如下:1:定义⽗类functionParent(){}2:定义⼦类funtion Son(){}3:通过原型给Object对象添加⼀个扩展⽅法。
Object.prototype.customExtend =function(parObj){for(variinparObj){//通过for-in循环,把⽗类的所有属性⽅法,赋值给⾃⼰this[i] =parObj[i];}}4:⼦类对象调⽤扩展⽅法Son.customExtend(Parent);三、使⽤call和apply实现继承⾸先,要使⽤这种⽅式显⽰继承,我们再来回顾⼀下call和apply两个函数的作⽤:call和apply:通过函数名调⽤⽅法,强⾏将函数中的this指向某个对象;call写法:func.call(func的this指向的obj,参数1,参数2...);apply写法:func.apply(func的this指向的obj,[参数1,参数2...]);那么,我们使⽤这两个函数实现继承的思路就是:在⼦类中,使⽤⽗类函数调⽤call或apply,并将⽗类的this,强⾏绑定为⼦类的this。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JavaScript巩固与加强四目录JavaScript巩固与加强四 (1)一、原型链 (2)1、原型对象 (2)2、原型对象的作用 (2)3、原型对象的应用场景 (2)4、探究原型对象从何而来 (3)5、原型链 (4)二、Object类 (5)1、Object类是所有类的基类(原型继承中已被证明) (5)2、使用Object类创建自定义对象 (6)三、静态属性和静态方法 (6)1、什么是静态属性 (6)2、静态属性说明 (6)3、静态方法 (7)4、静态方法作用 (7)四、函数闭包 (8)1、什么是函数闭包 (8)2、函数闭包的作用 (8)3、在全局作用域中访问局部变量 (8)4、Javascript垃圾回收机制(引用计数器) (9)5、使用函数闭包访问局部变量 (9)一、原型链1、原型对象在Javascript中,每个构造函数在加载后都会在内存中自动生成一个原型对象,我们可以通过prototype属性来访问该对象。
Person构造器与Person原型对象在内存中表现为相互独立,但是在Person构造函数的内部存在一个prototype属性指向了Person原型对象。
同理,在Person原型对象的内部也存在一个属性constructor其指向了Person构造器。
2、原型对象的作用当构造器的实例化对象p1访问一个不存在的属性或方法时,系统会自动到当前构造器所指向的原型对象中去寻找。
说明:Person构造器的实例化对象p1访问一个不存在的属性constructor,系统会自动到当前Person构造器的原型对象中去寻找constructor属性,由于原型对象中的constructor属性指向了Person构造器,所以当我们调用p1.constructor时,系统会自动弹出Person构造器。
3、原型对象的应用场景在实际项目开发中,我们可能会经常使用系统类或别人定义好的类文件,如果发现类中并不存在我们需要的属性或方法,我们不能直接修改源代码,也不愿为每个对象都单独定义相关属性和方法,那么不妨考虑使用原型对象对其进行扩展。
例1:使用原型对象,扩展自定义类例2:扩展数组类的功能,为每一个数组对象添加一个方法,可以查找某个元素的所在位置var arr = [10,20,30,40,50];arr.find(30); //返回30所在数组的索引下标24、探究原型对象从何而来问题:既然原型对象也是一个对象,那么其又是由哪个类实例化而来呢?记住:所有的原型对象在实例化时都会自动执行以下代码(构造器名称.prototype)= new Object();所以,由以上代码可知:所有的原型对象都是Object类的实例化,在Javascript中,Object 是所有类的基类(父类)。
所有的原型对象都是由Object构造器实例化而来的,所以原型对象都会自动继承Object类中所有属性和方法,又由于某个类的实例化对象(如p1对象)访问一个不存在的属性或方法时,系统首先到当前构造器的原型对象中去寻找,由于原型对象又继承了Object类,所以实例化对象也会自动继承Object类中的所有属性或方法,我们把这种继承关系就称之为“原型继承”。
证明:原型继承的存在所以可以得出结论:所有的对象(无论是系统类对象还是自定义对象)都会自动继承Object 类中的所有属性和方法,所以Object是所有类的基类(父类)5、原型链当Person构造器的实例化对象p1访问一个不存在的属性或方法时,系统会自动到Person 构造器所指向的原型对象中去寻找,如找不到则会继续向上一级构造器所指向的原型对象中去寻找,直到Object构造器所指向的原型对象,我们把这种链式的查询关系就称之为“原型链”。
证明:原型链的存在二、Object类在Javascript中,Object类是所有类的基类,使用Object类来创建自定义对象时可以无需指定构造函数。
1、Object类是所有类的基类(原型继承中已被证明)说明:由于所有的原型对象都是Object类的实例,所以原型对象会自动继承Object类中的所有属性和方法,又由于我们的实例化对象(如p1)访问一个存在的属性或方法时,根据原型链的原则,系统会自动到当前构造器所指向的原型对象中去寻找,由于原型对象又继承了Object类,所以p1对象也会自动继承Object类中的所有属性和方法,所以由此可以得出结论:Object类是所有类的基类(父类)2、使用Object类创建自定义对象我们知道,在Javascript中如果我们想创建一个对象来保存更多的数据就必须定义一个构造器函数,即使其是空的也需要定义。
在实际项目开发中,如果我们只是想获得一个对象而不关心其是哪个类的实例,不妨考虑使用Object来实例化对象。
三、静态属性和静态方法1、什么是静态属性在有些面向对象的编程语言中,可以通过static关键词来定义静态属性。
这一点,在Javascript 中可以通过“模拟”的形式来实现,基本语法:静态属性= 类名.属性2、静态属性说明在一个类中一共存在两种属性:实例属性与静态属性实例属性:实例属性是对象所特有的某个属性,如姓名、年龄、邮箱、家庭住址。
静态属性:静态属性并不是某个对象的所特有的,而是所有对象所拥有的属性,如班级总人数。
例1:定义一个计数器,实现对学生的统计例2:改进例1,实现自动计数功能3、静态方法在有些面向对象的编程语言中,可以在方法的前面添加一个static关键词来实现静态方法,但是在Javascript并不存在static关键词,但是我们可以通过模拟的形式来实现,基本语法:类.属性= function() {}4、静态方法作用在实际项目开发中,我们定义的静态方法主要用于操作静态属性。
例3:改进例2,使用静态方法操作静态属性四、函数闭包1、什么是函数闭包所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
2、函数闭包的作用①可以在全局作用域中访问局部变量②可以让局部变量一直驻留在计算机的内存中3、在全局作用域中访问局部变量例1:在全局作用域中无法访问局部变量以上程序无法直接运行,是错误的,主要受到两方面的影响:①作用域不允许②受到Javascript垃圾回收机制的影响,当display函数运行完毕后,其内部的局部变量和函数都会自动被内存所回收。
4、Javascript垃圾回收机制(引用计数器)系统中的每块内存都有一个引用计数器,默认其值为0。
当全局作用域中有变量对其引用时,其内置的引用计数器进行+1操作,当Javascript中的垃圾回收机制运行时,系统首先会判断当前内存的引用计数器是否为0,如果为0代表没有引用,则自动回收。
5、使用函数闭包访问局部变量在Javascript中,可以把函数在内存中的首地址作为函数的返回值直接返回。
例1:以上程序的运行结果:hello问题:为什么display函数运行完毕后,其内部的fn函数还没有被回收呢?答:正常情况下,display函数运行完毕后,其内部的变量和函数都会自动被Javascript中的垃圾回收机制所回收。
但是上题中,由于在全局作用域中有一个test变量实现了对fn函数的首地址的引用,所以在内存中,fn函数在内存中的引用计数器要进行+1操作,当Javascript 中的垃圾回收机制运行时,其并不会被回收,所以我们可以直接访问fn函数。
例2:使用以上特性实现对局部变量的访问运行结果:10说明:由于受到test全局变量的影响,fn函数并不会被内存所回收,又由于fn函数引用了一个变量i,根据作用域链的原则,系统会自动到上一级作用域寻找var的声明语句,找到了var i = 10则直接使用,所以其也会导致局部变量i也不能被内存所回收,从而实现对局部变量的访问,我们把这个函数就称之为“闭包函数”。
6、让局部变量一直驻留在计算机内存中说明:函数闭包一共有两个特性(①访问局部变量②让变量一直驻留在计算机内存中)当执行var test = display()完毕后,系统会将fn函数在内存中的首地址赋值给去全局变量test,又由于fn函数引用了局部变量i,所以会导致变量i也不能被内存所回收,当我们执行test(),系统会自动调用局部变量i然后进行++操作,所以第一次弹出结果为11,当第二次执行test()函数时,系统会自动调用fn函数,由于fn函数中执行alert(++i),又由于局部i一直没有被内存所回收,所以其值为11,进行++操作则变为12,然后直接输出,依次类推,第三次弹出13,第四次弹出14,所以由此可知:闭包会导致局部变量一直驻留在计算机内存中。
7、练习题(提示:使用闭包解决)五、私有属性1、私有属性的定义在有些面向对象的编程语言中,我们可以通过private关键词来实现私有属性的定义,这一点,在Javascript中可以通过“模拟”的方式实现。
回顾PHP中的三种属性:public 公有的protected 受保护的private私有的记住:在Javascript中,只有公有和私有属性,而没有受保护的属性。
在构造函数的内部,通过this声明的属性就是公有属性,而通过var声明我们就称之为“私有属性”。
例1:定义私有属性问题:为什么在上题中,无法直接访问私有属性love呢?答:1)作用域不允许2)受到Javascript垃圾回收机制的影响3)没有为lindaiyu这个对象赋予love属性2、私有属性的访问(闭包)与设置①访问私有属性②设置私有属性在实际项目开发中,如果一个私有属性,只有一个set方法,其就是只写属性,如果只有一个get方法,其就是一个只读属性。
六、call与apply函数1、为什么需要call与apply函数Javascript中,每一个函数内部都有一个特殊的关键词this,其随着所处环境的不同其指向也是不同的。
函数的内部其this也是指向window对象行内绑定:其函数内部的this指向window对象动态绑定:其函数内部的this指向当前正在操作的dom对象问题:我们可不可以人为的更改函数内部的this指向呢?答:可以,使用call或apply方法。
2、call或apply函数的定义①call([thisObj[,arg1[,arg2[,argN]]]])参数说明:thisObj:要指向的对象arg1,arg2…argN :参数列表②apply([thisObj[,argArray]])参数说明:thisObj :要指向的对象argArray :参数数组,要求是一个数组问题:call方法与apply方法有何区别?答:call方法与apply方法功能是完全一致的,都是为了改变函数内部的this指向,唯一的不同就是语法的不同。