JavaEE03_JavaScript
JavaEE03_JavaScript
1、使用javascript
1.1 js引入
Javascript代码写在script标签内,且script标签s必须成对出现,不能自闭和,不然会出错
<script type="text/javascript">script>
引入外部 文件名.js代码(src后跟路径)
<script src="js/qj.js">script>
1.2 基本语法
语法基本与java一致,不过js的变量只有var和let
var是全局bianl
let是局部变量
可以通过console.log()在浏览器控制台输出(浏览器F12)
alert()弹出弹窗
var的基本测试
/*** 基本类型var:* 1.number:js不区分小数和整数* 123 整数123* 123.1 浮点数123.1* 1.123e3 科学计数法* -99 负数* NaN not a number* Infinity 表示无限大* 2.字符串:'abc' "abc"* 3.布尔值:true false(与java用法一样)* 4.逻辑运算:&&* ||* !* 5.比较(重要)* = 赋值* == 等于(类型不一样,值一样,也会判断为true)* === 绝对等于(类型一样,值一样才会判断为true)* 避免使用==,容易出问题* 6.NaN 与所有数值都不相等,包括自身* 只能通过NisNaN(NaN)来判断这个数是否为NaN** 7.浮点数问题* console.log((1/3)===(1-2/3)); 因为精度会缺失,所以输出falseconsole.log(Math.abs((1/3)-(1-2/3))<0.00000001); 精度小于0.00000001,默认两个相等* 8.null和undefined* null 空* undefined 未定义* 9.数组:java必须是一系列相同类型的对象,在JS中并不需要这样,因为var是所有类型,如果越界就会undefined* var arr=[1,2,3,4,'name',true,null];* console.log(arr[1]);* 10.对象:对象是大括号,数组是中括号* 每个属性之间使用逗号隔开,最后一个不需要添加* 输出:person.name*/
1.3严格检查模式
<script>'use strict'
script>
前提:IDEA 需要设置支持ES6语法
‘use strict’ 严格检查模式,预防js的随意性导致产生的一些问题,比如直接写i=1;没有去定义它为 var或let也是可以使用的,且默认是var
必须写在javascript的第一行
2、js详解
2.1字符串
-
正常字符串一般使用单引号或双引号包裹。
-
转义字符:
/**** 2、转义字符* \'* \n* \t* \u4e2d // \u#### Unicode编码* \x41 //ascll**/
console.log('a\'');
console.log("a\nd");
console.log("\u4e2d\n\x41");
-
多行字符串编写(tab上面的 ` 键)
let msg= ` xiaosi world ` console.log(msg) -
模板字符串
let name="xiaosi"; let msg2=`你好,${name}` -
字符串长度:.length
-
字符串的不可变性
var myStr = "Bob"; myStr[0] = "J";//这是无法实现的,字符串创建完单个值是无法改变的,要改变要重新给它赋一个值var myStr = "Bob"; myStr = "Job"; -
大小写转换
.toUpperCase()转大写
.toLowerCase()转小写
-
显示某字符的下标
.indexOf(’ ')
-
substring
.substring(n,m) 从第n个字符串截取到最后m个字符串,若m没写就是到最后一个字符串[n,m)
2.2 数组
-
长度:给arr.length赋值,数组大小就会发生变化,自动添加undefined,如果赋值过小,元素就会丢失
arr.length=10; -
indexOf,通过元素获得下标索引,字符串的"1"和数字的1是不一样的,字符串的"1"是获取此字符串的下标值,1是获取下标1的字符串
-
slice()截取Array的一部分,返回一个新数组,类似String中的substring
数组.slice(n) :从下标n开始截取,到最后一个字符,且原数组不会发生改变
-
push pop
arr2.push("a","b");//压入尾部 console.log(arr2); arr2.pop();//弹出尾部 console.log(arr2); -
unshift() shift()
arr2.unshift("a","b");//压入头部 console.log(arr2); arr2.shift();//弹出头部 console.log(arr2); -
排序sort()
sort() 方法用于对数组的元素进行排序。
排序顺序可以是字母或数字,并按升序或降序。
默认排序顺序为按字母升序。
-
元素反转
.reverse()
-
拼接 concat,并没有修改数组,只是会返回一个新的数组
arr3.concat(arr2) //不会改变原数组 -
连接符join,打印拼接数组,使用特定的字符串连接
console.log(arr3.join('-')); //结果C-B-A -
多维数组
let arr4=[ [1,2],[3,4],["5","6"] ]; console.log(arr4[1][1]);
2.3 对象
var 对象名={
属性名:属性值,
属性名:属性值
}
var person={//多个属性用逗号隔开,最后一个不加逗号name:'xiaosi',age:3,email:"1170035083@qq.com",score:0}
//对象赋值
person.name='haishi';
//使用一个不存在的对象属性,不会报错,会undefined
console.log(person.haha);
//动态删除属性,通过delete删除对象属性,可以在控制台直接删除
delete person.score;
//动态添加,直接给新的属性添加值就可以
person.haha="haha";
//判断属性值是否在对象中 xxx in xxx
console.log('name' in person);
console.log('toString' in person);//true 可以知道它父类的方法
//判断一个属性是否是这个对象自身拥有的hasOwnProperty
console.log(person.hasOwnProperty('name'));//true
console.log(person.hasOwnProperty('toString'));//false
Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
pereson里面有name属性,虽然他的父类有toString但是他自己没有,所以是false
2.4 流程控制
除了foreach,其余的与java一致
//判断
var age=3;if(age>3){alert("haha");}else{alert("kuwa");}
//循环
while(age<100){age++;console.log(age);
}
//for循环
for(let i=100;i<110;i++){console.log(i);
}
//foreach循环,对象可以value.xxx
var arr=[1,2,3,34,54,6,7,12,32];
arr.forEach(function (value) {console.log(value);
})
/*
* 在java中,for(Type str:el){}
* */
2.5 Map和Set
与java一致
//ES6的新特性
//学生的成绩,学生的名字
var map=new Map([['tom',100],['jack',90],['haha',80]]);
var name=map.get('tom');
console.log(name);
map.set('admin',10);
console.log(map);
map.delete('tom');var set=new Set([3,1,1,1,1]);//set可以去重
set.add(2);
console.log(set);
set.delete(1);
console.log(set);
console.log(set.has(3));//里面是否包含x
2.6 iterator
// for in只能打印下标,新增下标不能正常显示下标,会直接显示里面的值
//若for in要打印数值的话,用下面实现
for(var num in arr){console.log(arr[num]);//这里的num只是下标
}//通过for of来打印数 遍历map 遍历set都是一样的
for(let x of arr){console.log(x);
}
2.7 定义函数
//定义方式1:
//绝对值函数,一旦执行到return就表示函数结束,返回结果
//如果没有执行return,函数执行完也会返回结果,结果是undefined
function aaa(x) {if(x>=0){return x;}else{return -x;}
}
/定义方式2:(与上一种等效)
/*
* function(x) {...}是一个匿名函数,但是可以把结果赋值给abs,通过abs就可以调用函数
* */
var abs= function(x) {if(x>=0){return x;}else{return -x;}
}
/*** 参数问题:js可以传任意个参数,也可不传递参数* 参数进来是否存在 假设不存在参数,如何规避(手动抛出异常)* 运算数为数字 typeof(x) = "number"字符串 typeof(x) = "string"布尔值 typeof(x) = "boolean"对象,数组和null typeof(x) = "object"函数 typeof(x) = "function"*/
function abc(x) {if(typeof x!=='number'){throw 'Not a Number';}if(x>=0){return x;}else{return -x;}
}
/*** arguments是一个JS免费赠送的关键字,代表传递进来的参数,是一个数组* 问题:arguments包含所有的参数,有时想使用多余的参数来进行附加操作,需要排除已有的参数* 若传入多个参,这些参数就会被自动放入arguments数组中,可以直接从arguments中拿出来*/
function bbb(x) {console.log("x->"+x);for(let i=0;i<arguments.length;i++){console.log(arguments[i]);}if(x>=0){return x;}else{return -x;}
}
2.8 可变长参数
以前的写法:
//以前的写法,使用arguments来判断aaa()里到底输入了多少个值,因为js是一款极其不严谨的语言,就算你传入一
//百个值都不会报错。所以可以使用arguments来执行传入多参的方法
function aaa(a,b) {console.log("a=>"+a);console.log("b=>"+b);if(arguments.length>2){for(let i=2;i<arguments.length;i++){console.log(arguments[i]);}}
}
由于ES6引入的新特性,可以直接写可变长参数:
//现在
function bbb(a,b,...rest) {//rest只能写在最后面,必须用...标识console.log("a=>"+a);console.log("b=>"+b);console.log(rest);
}
2.9 变量的作用域
- 全局作用域:所有地方都可以访问
- 函数作用域:只能在函数内部访问
变量的声明:显示声明(var i=1;)和隐式声明(i=1;)
-
在函数体中声明的变量,在函数体外不能使用,要用得使用闭包
-
如果两个函数用了同一个变量名,只要在函数内部声明就不冲突
-
内部函数成员可以访问外部函数,外部不能访问内部
-
内部函数变量和外部函数变量重名的情况下,内部与外部重名的部分用内部的变量。。。。由内向外查找
-
假设外部存在这个同名的函数变量,则内部函数会屏蔽外部函数的变量
2.10 作用域的提升
下面的定义的变量上面可以调用(作用域提升),但是仅仅只是提升作用域,它的赋值并不会被提升上去。
var x='x'+y;
console.log(x);
var y='y';//结果:xundefined,js的执行引擎,自动提升了y的声明但是不会提升变量y的赋值,在js建立之初就存在的//养成规范:所有的变量定义都放在函数的头部,不要乱放,便于代码维护
2.11 alert的一些方法
alert(a);
alert(window.a);//默认所有全局变量,都会自动绑定在Window对象上
window.alert(a);//alert()这个函数本身也是Window变量
//把alert方法赋予别的变量,原来的alert失效
var old_alert=window.alert; //把alert方法赋给一个变量,这个变量就有了alert的功能
old_alert('haishi');//重写alert方法
window.alert=function () {
};//恢复
window.alert=old_alert;
JS实际上只有一个全局作用域,任何变量(函数也可视为变量),假设没有在函数作用范围内找到,就会向外查找,
如果在全局作用域都没有找到,报错ReferenceError
2.12 自定义唯一全局变量
由于我们所有的全局变量都会绑定到我们的window上,如果不同的js文件使用了相同的全局变量,就会造成冲突。解决办法:自定义唯一全局变量,把自己的代码全部放入自己定义的唯一空间名字中,降低全局命名冲突的问题
//唯一全局变量
var XiaoSi={};//定义全局变量或方法
XiaoSi.name='xiaosi';
XiaoSi.add=function(a,b){return a + b ;
}
全局作用域冲突使用自定义全局变量来解决,而局部作用域冲突问题则使用let来解决。
2.13 常量
在ES6之前,定义常量:只有用全部大写字母命名的变量就是常量;建议不要修改这样的值,但实际上是可以随意修改的。ES6我们可以const来实现真正的常量(类似java的final)
const PI='3.14'; //只读变量
console.log(PI);
PI='3.1415';//报错Uncaught TypeError: invalid assignment to const 'PI'
2.14方法
方法就是把函数放在对象里面,对象只有两个东西:属性和方法
var xiaosi={name:'小厮',birth:1998,//方法age:function () {//今年-出生的年份var now=new Date().getFullYear();return now-this.birth;}
}
也可以在对象中引入外面的函数。
function getAge() {var now=new Date().getFullYear();return now-this.birth;
}
var xiaosi={name:'小厮',birth:1998,//引入外部函数的方法age:getAge
}
3、扩展
3.1 typeof
console.log(typeof 123); //查看后面的东西是什么类型的(这里是number)
3.2 Date
var now=new Date();
now.getFullYear();//年
now.getMonth();//月 0_11代表月
now.getDate();//日
now.getDay();//星期几
now.getHours();//时
now.getMinutes();//分
now.getSeconds();//秒now.getTime(); //时间戳 全世界统一 1970 1.1 0:00:00 毫秒数console.log(new Date(1578106175991));//把时间戳转换为时间
/*** 转换*/
now=new Date(1578106175991);
console.log(now.toLocaleString());//调用一个方法,而不是属性
//now.toGMTString()格林威治标准时间
3.3 Json
/*** JSON一种轻量级的数据交换格式* 具有层次结构** 在JS中一切都是对象、任何js支持的类型都可以用JSON来表示* 对象{}* 数组[]* 键值对key:value*/
//对象转化为json字符串
var JSONUser=JSON.stringify(user);//json字符串转化为对象,参数为JSON字符串
var obj=JSON.parse('{"name":"xiaosi","age":3,"sex":"男"}');
3.4 js的面对对象编程
/*** 类:模板* 对象:具体的实例** 但在JS中,需要换一下思路:* 原型:*/
var user={name:'xiaosi',age:3,run:function () {console.log(this.name+" run...");}
};var Bird={fly:function () {console.log(this.name+" fly...");}
};var xiaoming={name:'xiaoming' //无法直接调用run方法,会报错
};/*
* 原型对象(实际上是继承)
* */
xiaoming.__proto__=user;//小明的原型是user,这是旧的方法,可以随意更换
xiaoming.run();
xiaoming.__proto__=Bird;
xiaoming.fly();
3.5 class的继承
在ES6之前无class关键字,所以要使用prototype往前面的函数添加新方法
prototype 属性使您有能力向对象添加属性和方法。
function Student(name) {this.name=name;}//给student新增一个方法,要么改它的方法,要不如下获得它的原型增加...ES6之前的原生方法,不用记Student.prototype.hello=function () {alert('hello')}
//ES6之后
//定义一个学生的类
class Student{constructor/*构造器关键字*/(name) {this.name=name;}hello(){alert('hello');}
}
var xiaoming=new Student('xiaoming');
xiaoming.hello();
console.log(xiaoming.name);/*** 继承*/class XiaoStudent extends Student{constructor(name,grade) {super(name);this.grade=grade;}myGrade(){alert('我是一个小学生');}
}var xiaohong=new XiaoStudent('xiaohong');
xiaohong.myGrade();
console.log(xiaohong);
3.6 原型与原型链
原型
在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
让我们用一张图表示构造函数和实例原型之间的关系:

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
原型链
- 1.
__proto__和constructor
每一个对象数据类型(普通的对象、实例、prototype…)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。
function Person() {}var person = new Person()console.log(person.__proto__ === Person.prototype)//trueconsole.log(Person.prototype.constructor===Person)//true//顺便学习一个ES5的方法,可以获得对象的原型console.log(Object.getPrototypeOf(person) === Person.prototype) // true

image
- 2.何为原型链
在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。
举例说明:person → Person → Object ,普通人继承人类,人类继承对象类
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function Person() {}Person.prototype.a = 123;Person.prototype.sayHello = function () {alert("hello");};var person = new Person()console.log(person.a)//123console.log(person.hasOwnProperty('a'));//falseconsole.log('a'in person)//true
person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如 person.__proto__中也没有该属性,又该如何查找?
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类)在Object.prototype上没有__proto__这个属性。
console.log(Object.prototype.__proto__ === null) // true

4、BOM和DOM
DOM是一套操作HTML标签的API(接口/方法/属性)
BOM是一套操作浏览器的API(接口/方法/属性)
4.1 操作BOM对象
4.1.1 基本
BOM:浏览器对象模型
JS诞生就是为了能够让他在浏览器中运行
-
浏览器内核:
IE 6-11
Chrome
Safari
FireFox(Linux默认) -
三方:
QQ,360 -
window:
window代表浏览器窗口
window.innerHeight;
window.innerWidth;
window.outerHeight;
window.outerWidth;//浏览器的内宽高和外宽高,调整浏览器会改变
-
Navigator封装了浏览器的信息:
navigator.appName
“Netscape”
navigator.appVersion
“5.0 (Windows)”
navigator.userAgent
“Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0”
navigator.platform
“Win32”
大多数时候不会使用navigator对象,因为会被人为修改
不建议使用这些属性来判断和编写代码(例如if(navigator.appName=‘xxx’)之类的)
window.navigator;
- screen屏幕
screen.width;
screen.height;//像素
- location
/*** location(重要)* 代表当前页面的URL信息* host: "localhost:63342" 主机* href: "http://localhost:63342/JavaScript/lesson04/1.%E6%93%8D%E4%BD…87%8D%E7%82%B9%EF%BC%89.html?_ijt=onret8mf0ch9jg9gjfsdsv9vjv" 当前指向网页* protocol: "http:" 协议* reload: reload() 重新加载网页*/
console.log(location);//设置新的地址,直接转到assign指向的网站
location.assign('https://www.baidu.com');
4.1.2 document
/***document代表当前页面, HTMl DOM树*/
var dl=document.getElementById('app');//获取具体的文档树节点/*** 获取cookie* 劫持cookie原理* 恶意人员:获取你的cookie上传到他的服务器** 服务器端可以设置cookie:httpOnly(只读)*/
console.log(document.cookie);
4.1.3 history
history代表浏览器的历史记录,不建议使用
history.back() //后退
history.forward() //前进
4.2 操作DOM对象
4.2.1 基本
DOM:文档对象模型
核心:
浏览器网页就是一个DOM树形结构
1.更新:更新DOM节点
2.遍历dom节点:得到DOM节点
3.删除:删除一个DOM节点
4.添加:添加一个新的节点
要操一个dom节点就必须获得这个dom节点
4.2.2 获得DOM节点
document
<body>
<div id="father">
<h1>标题一h1>
<p id="p1">p1p>
<p class="p2">p2p>
div>
<script>//对应CSS选择器,获得dom节点var h1=document.getElementsByTagName('h1');var p1=document.getElementById('p1');var p2=document.getElementsByClassName('p2');var father=document.getElementById('father');var childrens=father.children; //获取父节点下的所有子节点//father.firstChild第一个节点//father.lastChild最后一个节点//这是原生代码,之后尽量使用jQuery();script>
body>
4.2.3 更新节点
var id1=document.getElementById('id1');
id1.innerText='456';//修改文本的值
id1.innerHTML='456';//可以解析HTML文本标签//可以进行CSS操作,在网页上可以直接用JS进行修改界面
id1.style.color='yellow';
id1.style.fontSize='200px';//不支持-,用驼峰命名
4.2.4 删除节点
/*** 删除节点的步骤:先获取父节点,再通过父节点删除自己* 第一种标准流程*/
var self=document.getElementById('p1');
var father=self.parentElement;
//father.removeChild(self);/*** 方法2:通过下标移除,要注意删除多个节点的时候children是在时刻变化的*/
//father.removeChild(father.children[1]);//你删除完一后原来的2就变成一
4.2.5 插入节点
我们获得了某个Dom节点,假设这个Dom节点是空的,我们通过innerHTML就可以增加一个元素了,但是这个Dom节点已经存在元素了,我们就不能这么干,会覆盖,可以使用追加:append
varjs=document.getElementById('js'),//已存在的节点list=document.getElementById('list');
//list.appendChild(js);//控制台输入,id为js的标签会变为div的子标签
/*** 通过JS创建一个新的节点,实现插入*/
var newP=document.createElement('p');//创建一个新标签
newP.id='newP';//标签id设为newP
/*newP.id='newP';等价于newP.setAttribute('id','newP');*/
newP.innerText='xiaosi';
list.appendChild(newP);
/*** 创建一个标签节点* -->
<script src="lib/jquery-3.5.1.js">script>
公式:$(selector).action(),例:
//选择器就是css的选择器
$('#test-jquery').click(function () {alert('hello jQuery');
});
5.2 选择器
//原生JS,选择器少,麻烦不好记
//标签
document.getElementsByTagName()
//id
document.getElementById()
//类
document.getElementsByClassName()//Jquery选择器 css中的选择器它都能用,不会的查网站
$('p').click()//标签选择器
$('#id1').click() // id选择器
$('.class1').click() // class选择器
5.3 事件
<html lang="en">
<head><meta charset="UTF-8"><title>Titletitle><script src="lib/jquery-3.5.1.js">script><style>#divMove{width: 500px;height: 500px;border: 1px solid pink;}style>
head>
<body>
mouse:<span id="mouseMove">span>
<div id="divMove">在这里移动鼠标试一下
div><script>//当前网页元素加载完毕之后响应事件,相当于公式的简化$(function () {$('#divMove').mousemove(function (e) {$('#mouseMove').text('x:'+e.pageX+'y:'+e.pageY);})});
script>
<div>div>body>
html>
5.4 操作dom元素
<html lang="en">
<head><meta charset="UTF-8"><title>Titletitle><script src="lib/jquery-3.5.1.js">script>
head>
<body>
<ul id="test-ul"><li class="js">JavaScriptli><li name="python">Pythonli>
ul><script>$('#test-ul li[name=python]').text();//获得ul下面name=python的值//$('#test-ul li[name=python]').text('aaa');//修改ul下面name=python的值$('#test-ul').html();//获得ul的html//$('#test-ul').html('123');//修改ul的html/*** css的操作*/$('#test-ul li[name=python]').css("color","pink");//要设置多个属性可以用键值对$('#test-ul li[name=python]').css({"color","pink"},{})/*** 元素的显示和隐藏:本质display:none*/$('#test-ul li[name=python]').show();$('#test-ul li[name=python]').hide();/*** 别的用法:可以通过查文档来了解*/$(window).width();//只要满足公式,这里是浏览器窗口的宽script>
body>
html>
6、闭包
概念:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
6.1 一个简单的闭包
function A(){function B(){console.log(``'Hello Closure!'``);}return B;
}
var C = A();
C();// Hello Closure!
函数A的内部函数B被函数A外的一个变量 c 引用。
也就是:
当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
6.2 用途
在 Javascript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收,否则这个对象一直会保存在内存中
也就是说,A 不会被 GC 回收,会一直保存在内存中。
function A() {var count = 0;function B() {count ++;console.log(count);}return B;
}
var C = A();
C();// 1
C();// 2
C();// 3
count 是函数A 中的一个变量,它的值在函数B 中被改变,函数 B 每执行一次,count 的值就在原来的基础上累加 1 。因此,函数A中的 count 变量会一直保存在内存中。
当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就可以用闭包来定义这个模块。
6.3 闭包的高级写法
(function (document) {var viewport;var obj = {init: function(id) {viewport = document.querySelector('#' + id);},addChild: function(child) {viewport.appendChild(child);},removeChild: function(child) {viewport.removeChild(child);}}window.jView = obj;
})(document);
这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。
功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行,需要重点理解的是这种写法是如何实现闭包功能的。
可以将上面的代码拆分成两部分:(function(){}) 和 () 。
第1个**()** 是一个表达式,而这个表达式本身是一个匿名函数,所以在这个表达式后面加 () 就表示执行这个匿名函数。
因此这段代码执行执行过程可以分解如下:
var f = function(document) {var viewport;var obj = {init: function(id) {viewport = document.querySelector('#' + id);},addChild: function(child) {viewport.appendChild(child);},removeChild: function(child) {viewport.removeChild(child);}}window.jView = obj;
};
f(document);
在这段代码中似乎看到了闭包的影子,但 f 中没有任何返回值,似乎不具备闭包的条件,注意这句代码:
window.jView = obj;
nction B() {
count ++;
console.log(count);
}
return B;
}
var C = A();
C();// 1
C();// 2
C();// 3
count 是函数A 中的一个变量,它的值在函数B 中被改变,函数 B 每执行一次,count 的值就在原来的基础上累加 1 。因此,函数A中的 count 变量会一直保存在内存中。当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就可以用闭包来定义这个模块。### 6.3 闭包的高级写法```javascript
(function (document) {var viewport;var obj = {init: function(id) {viewport = document.querySelector('#' + id);},addChild: function(child) {viewport.appendChild(child);},removeChild: function(child) {viewport.removeChild(child);}}window.jView = obj;
})(document);
这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。
功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行,需要重点理解的是这种写法是如何实现闭包功能的。
可以将上面的代码拆分成两部分:(function(){}) 和 () 。
第1个**()** 是一个表达式,而这个表达式本身是一个匿名函数,所以在这个表达式后面加 () 就表示执行这个匿名函数。
因此这段代码执行执行过程可以分解如下:
var f = function(document) {var viewport;var obj = {init: function(id) {viewport = document.querySelector('#' + id);},addChild: function(child) {viewport.appendChild(child);},removeChild: function(child) {viewport.removeChild(child);}}window.jView = obj;
};
f(document);
在这段代码中似乎看到了闭包的影子,但 f 中没有任何返回值,似乎不具备闭包的条件,注意这句代码:
window.jView = obj;
obj 是在函数 f 中定义的一个对象,这个对象中定义了一系列方法, 执行window.jView = obj 就是在 window 全局对象定义了一个变量 jView,并将这个变量指向 obj 对象,即全局变量 jView 引用了 obj . 而 obj 对象中的函数又引用了函数 f 中的变量 viewport ,因此函数 f 中的 viewport 不会被 GC 回收,viewport 会一直保存到内存中,所以这种写法满足了闭包的条件。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
