js的运行机制
- 在js中js引擎会优先解析var变量和function定义,在预解析完成后从上到下逐步进行
- 解析var变量时会把值存储在执行环境中,而不会去赋值,值是存储作用!如alert(a);
var a = 5;这时会输出undifiend,意思是没有被初始化没有被赋值。这并不是没有被定义,错误的意思 - 在解析function时会把函数整体定义,这也就解释了为什么在function定义函数时为什么可以先调用后声明了!其实表面上看是先调用了,其实在内部机制中第一步实行的是把以function方式定义的函数先声明了!
<script>
name="lisi";
function ff(){
alert(name);
// var name=123;
let name=999;
}
ff();
</script>
ES6中箭头函数的调用
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作 构造函数
(参数1, 参数2, …, 参数N) => { 函数声明 }
//相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
(参数1, 参数2, …, 参数N) => 表达式(单一语句)
// 当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明}
// 没有参数的函数应该写成一对圆括号。
() => {函数声明}
<script>
//es5中:
var x = 11;
var obj = {
x: 22,
say: function () {
console.log(this.x)
}
}
obj.say(); //22
//而在es6中
var x = 11;
var obj = {
x: 22,
say: () => {
console.log(this.x);
}
}
obj.say(); //11
</script>
<script>
var bb = {
pp: function () {
console.log(this); //this是Bb对象
}
}
bb.pp();
var obj = {
pp: () => {
console.log(this);//是window,而不是obj对象 [重点]
}
/*
- 箭头函数不可以当作构造函数使用,也就是不能用new命令实例化一个对象,
否则会抛出一个错误
- 箭头函数的this是和定义时有关和调用无关,无论使用apply还是call都无
法改变
- 调用就是函数调用模式
*/
}
obj.pp();
</script>
<script>
(() => {
console.log(this)//window
})()
let arrowFun = () => {
console.log(this)//window
}
arrowFun()
let arrowObj = {
arrFun: function () {
(() => {
console.log(this)//arrowObj
})()
}
}
arrowObj.arrFun();
</script>
JS中内存空间释放的问题
- 谷歌浏览器:开辟一个内存,可能或有一些其他的变量等占用了这个内存,谷歌浏览器都会间隔一段时间看这个内存还有没有被占用,如果发现有没有被占用的内存了,就自己回收了(内存释放)
- 火狐和IE:开个内存当引用了它,就在内存中记录一个数,增加一个引用浏览器就把这个数+1,减少一个引用,浏览器就把这个数-1…当减到零的时候浏览器就把这个内存释放了;但是有些情况下(尤其是IE)记着记着就弄乱了,内存就不能释放了–>浏览器的内存泄露
- 一般情况下,函数执行形成一个私有的作用域,当执行完成后就销毁了->节省内存空间
- 函数执行返回一个引用数据类型的值并且在函数的外面被别人接收了,那么当前函数形成的私有作用域就不在销毁了
- var obj={};建议养成一个好的习惯,当obj这个对象使用完成了,手动的obj=null (null空对象指针),浏览器会自己把刚才的堆内存释放掉
类基础定义
<script>
function Student() {
this.toString=function(){
return "x";
}
}
var s1 = new Student();
s1.name = "子";
s1.age = 9;
// alert(s1);
console.log(s1);
//覆盖定义
s1.toString=function(){
return this.name+"---"+this.age;
}
console.log(s1.name + "---" + s1['name']);
console.log(s1.toString());
/*
访问对象公开属性的方式有两种:
- 对象名.属性名
- 对象名['属性名']
*/
</script>
<script>
var s2=new Student;
s2.score=999;
s2.aname="aaa";
console.log(s2);
console.log(s2.toString());
</script>
方法的覆盖
<script>
function Student(){ //构造器
this.name="猪猪侠";
this.sleep=function(){
return "我要吃饭";
}
}
var a=new Student;
console.log(a.name);
//在对象中进行方法覆盖
a.sleep=function(){
return "我睡觉";
}
console.log(a.sleep());
var b=new Student;
console.log(b.sleep());
console.log(Student.prototype)
//通过原型对象可以新增加方法,但是不能实现方法的覆盖定义
Student.prototype.sleep1=function(){
return "我要糖";
}
console.log(a.sleep());
var c=new Student();
console.log(c.sleep());
console.log(Student.prototype)
//js函数不支持重载
</script>
自定义类
使用this关键字修饰的变量不再是局部变量,它是该函数的实例属性
<script>
function Student(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
let bb = new Student("松鼠", 3, true);
console.log(bb)
let ss = bb.name + "," + bb.age + "," + (bb.sex ? '男' : '女');
console.log(ss);
//更加簡化的定義方式
var pan = {};
//添加屬性
pan.name = "老鼠";
pan.age = 2;
pan.sex = false;
console.log(pan);
</script>
<script>
function Person(name, age) {
//实例对象每个实例都不同,可以通过new Person("",19).name的方式访问
this.name = name;
//类属性,是类的所有实例公用,只能通过Person.nation的方式访问,
//而不能使用new Person("yan",18).nation方式访问
// Person.nation = "汉族";
Person.nation=age;
var bb = 0;//局部变量,外面不能访问,类似局部函数
}
let p1=new Person("zhangsan","白族");//new Person;
let p2=new Person("吉吉","汉族");
console.log(p1);
console.log(p2);
console.log(Person.nation);
//注意:局部变量在函数执行完毕后销毁,全局变量在页面
//关闭后销毁,函数内没有使用var声明的变量为全局变量
//直接定义一个对象
var p = { //JSON格式定義
//this.pp=function(){}可以用於function p(){}定義中
pp: function () {
for (var k = 0; k < 10; k++)
document.writeln("快快的跑...");
}
}
//调用方式
p.pp();
</script>
JS中的对象
JavaScript中可以使用以下几种对象
- JavaScript内置对象,如Date、Math及String
- 用户自定义的对象
- 由浏览器根据页面内容自动提供的对象
- 服务器上固有的对象。
闭包
函数定义在另外一个函数内部可以达到限制访问的目的
<script>
function func() {
var num = 0; //声明局部变量:num
function f() { //嵌套函数,在作用域里
console.log(++num)
}
return f; //调用嵌套函数f,并将f的执行结果返回
}
var ff = func();
window.ff(); //输出:1
ff(); //输出:2
ff(); //输出:3
</script>
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内。
通常说的闭包是当一个函数嵌套另一个函数,外部函数将嵌套函数对象作为返回值返回的时
候,我们把这种情况称为闭包
注意:
- 当一个函数func()创建后,它保存了一个作用域链,并且作用域链会被函数func()中的作用域中可访问的数据对象num填充
- 执行此函数func()时会创建一个称为“运行期上下文(execution context)”的内部对象,运行 期上下文定义了函数执行时的环境。并且运行期上下文对应自己的作用域链,同时它的作用域链
初始化为当前运行函数func()的[[Scope]]所包含的对象 - 然后执行环境会创建一个活动对象(call object),用来报讯局部变量,并把这个对象添加至作用域链[[Scope]]中。假如不存在嵌套函数,也没有其他引用的时候,活动对象就会被当做垃圾 回收掉。但是这里有嵌套函数f(),并将函数f()作为返回值返回时,就会又一个外部引用指向 这个嵌套函数f(),活动对象就不会当做垃圾回收掉,并且活动对象指向的变量所绑定的对象也不会被回收。
在实际开发中,闭包主要是用来封装变量实现公有私有变量,收敛权限。
优点:
- 变量长期驻扎在内存中
- 避免全局变量的污染
- 私有成员的存在
缺点:
- 因为不会被GC回收,所以常驻内存,会增大内存的使用量,使用不当会造成内存泄露。