JS深入学习

判断数据类型

1.typeof关键字,返回的是数据类型的字符串表达

var a;

console.log(typeof a);//输出'undefined'

注意:null、array、object返回object,function返回function

 

2.istanceof,检查一个对象是否是另一个对象的实例即a是否是b的实例,Object、Function、Array

a instanceof b,a是实例,b是构造函数

如果B函数的显示原型对象(B.prototype)在A对象的原型链上,返回true

 

3.===

console.log(a===undefined); //true

console.log(typeof a=='undefined');//true

var a=null; a===null;返回true;

 

undefined和null的区别?

underfined代表定义了没有赋值,null代表定义并赋值了,只是值为null

 

什么时候给变量赋值null?

将要赋值为对象前,赋值null

或者想让对象被垃圾回收,赋值null

 

 

栈里存:全局变量和局部变量,函数名

堆里存:对象,函数 

 

注意:n个引用变量指向同一个对象,通过一个变量修改对象的内部数据,其他所有变量看到的是修改后的数据

 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象。

var a={ name:'lkq'}

var b=a;

a={ name:'zyy',age: 13}//相当于创建一个新对象,让a指向这个对象

console.log(b.name,a.name,a.age); // 'lkq'   'zyy'  13

 

var a={age:12};

a={name:'zyy',age:13};

function fn(obj){

     obj={age:15};//这里相当于又创建一个新的对象,让a指向这个对象,但是这个obj是局部变量,函数执行完毕后,被删除,相                             //当于没有这一步,如果obj.age=15;就可以输出15,相当于直接在原对象上修改值

 }

  fn(a);

 console.log(a.age);//输出13

 

在js调用函数时传递变量参数是值传递还是引用传递?

可能值传递,可能引用传递;

        var a=2;

        function fn(a){

           a=a+1;

        }

        fn(a);

        console.log(a);//输出2

原理同上;

 

JS引擎怎么管理内存?

1.内存的生命周期:

分配内存空间,得到它的使用权

存储数据,反复操作

释放当前内存空间

2.释放内存

局部变量,函数执行完自动释放

对象:成为垃圾对象---》垃圾收集齐回收

全局变量不会释放

 

怎么访问对象内部数据?

.属性名:写法简单,有时候不能用

['属性名'],写法复杂,通用

什么时候必须用【‘属性名’】?

1.属性名包含特殊字符,比如"-" " ",p.content-type='';这样是不行的,因为属性名带"-"。

2.属性名不确定。比如var a='hello';var sex='男'; p.a=sex; 这样不行

 

怎么调用函数?

1.test():直接调用

2.obj.test():通过对象调用

3.new test():new 调用

4.test.call(obj) 或test.apply(obj):

补充:使用工厂方法创建对象

把属性传过去,返回一个对象;使用工厂方法创建的对象,使用的构造函数都是Object

缺点:使用工厂方法创建的对象,使用的构造函数都是Object。我们想让Person类的对象是Person这种。

function createPerson(name,age,gender){

 var obj=new Object();

 obj.name=name;

 obj.age=age;

 obj.gender=gender;

return obj;

}

var p=createPerson("...","....","....");

 

slice和splice

slice:提取数组中指定的元素

语法:arrObject.slice(start,end)

start:必须。从何处选取,如果负数,从数组尾部开始算起的位置。即-1,最后一个元素。

end:可选。如果没有这个参数,就选从start到数组结束的全部元素,如果参数是负数,从数组的尾部算起的位置。

返回一个[start,end)的新数组

 

splice:删除数组中的指定元素

语法:arrObject.splice(start,num,....)

start:表示开始位置的索引

num:表示删除的数量

第三个及以后的参数,可以传一些新的元素,这些元素将自动插入到开始位置索引的前面

会影响到原数组,返回删除后的结果

arr=["孙悟空","猪八戒","唐僧"];

var result=arr.splice(1,1,"牛魔王","红孩儿"); ---删除第1个元素,并把这两个插入到1之后

返回“孙悟空”,“牛魔王”,“红孩儿”,“唐僧”

 

call和apply

这两个方法都是函数对象的方法,需要通过函数对象调用。

调用call()和apply()可以修改函数执行调用对象的this

object.call(obj,2,3)

参数一:this指向的对象

参数二、三,....:传入的参数

object.apply(obj,[2,3])----需要将实参封装到数组里传递

 

回调函数

什么是回调函数?我定义的函数,但是我没有调用,它却执行了。

比如:

document.getElementById('btn').οnclick=function(){-------dom事件回调函数

  alert();

}

setTimeout(function(){-----定时器回调函数

alert();

},2000);

------还有ajax请求回调函数、声明周期回调函数

超时定时器

setTimeout(函数,时间)

 

IIFE

(立即执行函数表达式)

函数定义完,立刻被调用,这种函数叫立即执行函数,只会执行一次。

(function(){----匿名函数自调用

  var a=3;

  console.log(a + 3);---------写在外面会产生全局变量,写在里面不会产生,里面可以定义a,外面也可以,在相同作用域互不影响

})()

好处:隐藏实现;不会影响外部(全局)的命名空间;用它来编写js模块

 

原型对象

使用构造方法,里面写个函数。那么每调用一次构造方法创建出对象,就创建出一个新的方法,即使这个方法都是一样的。

------优化:将方法定义在全局作用域中,但是会污染全局作用域的命名空间。而且定义在全局中不安全。

我们创建的每一个函数,解析器都会向函数中添加一个属性,叫prototype;

这个属性对应一个对象,这个对象就是原型对象;

当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,这个隐含的属性指向该构造函数的原型对象。

我们可以通过__proto__来访问该属性。

function MyClass(){

 

}

var mc=new MyClass();

console.log(mc.__proto__);//==mc.protytype

原型对象就相当于一个公共的区域,所有同一类的实例都可以访问到这个原型对象。

我们可以将对象中共有的内容,同一设置到原型对象中;

MyClass.prototype.a=123;

当我们访问对象的一个属性或方法时,它会先在对象自身(mc)中寻找。如果有就直接使用,如果没有就去原型对象寻找,如果找到就直接使用。如果没有就去原型的原型中找,直到找到Object对象的原型,如果Object中都没有就返回undefined。

mc.hasOwnProperty("age");//用这个方法能判断对象自身是否有这个属性

例题:

function A(){

}

A.prototype.n=1;

var b=new A();

A.prototype={

 n:2,

 m:3

}

var c=new A();

console.log(b.n,b.m,c.n,c.m);// 1,undefined,2,3

 

执行上下文

全局执行上下文

1.在执行全局代码前,将window确定为  全局执行上下文

2.对全局数据进行预处理:

   var定义的全局变量---->undefined,添加为window的属性

  function声明的全局函数------>赋值,添加为window的方法

  this---------->赋值为window

3.开始执行全局代码

函数执行上下文

1.在调用函数,准备执行函数体之前,创建函数执行上下文对象(虚拟的存在于栈中)

2.对局部数据进行预处理

   形参---->赋值(实参)----->添加为执行上下文的属性

   arguments----->赋值(实参列表),添加为执行上文的属性

   var 定义的局部变量----->undefined,添加为执行上文的属性

   function声明的函数---->赋值(fun),添加为执行上文的方法

   this------>赋值(调用函数的对象)

3.开始执行函数体代码

 

执行上下文栈

1.在全局代码执行前,JS引擎创建一个栈来存储管理所有的执行上下文对象

2.在全局执行上下文(window)确定后,将它添加到栈中

3.在函数执行上下文创建后,将其添加到栈中

4.当前函数执行完后,栈顶对象出栈

5.所有代码执行完后,栈中只剩下window

案例:

var a=10;-------产生window执行上下文

var bar=function(x){

 var b=5;

 foo(x+b);// 这里执行不了,因为foo=,是赋值语句,只会把var foo提前,函数未执行

                 ---------------产生执行上下文对象

}

var foo=function(y){

  var c=5;

  console.log(a+c+y);

}

bar(10);----------产生执行上下文对象

作用域

有全局作用域、函数作用域、块作用域(ES6)

作用:隔离变量,不同作用域下同名变量不会冲突

注意:作用域在函数定义时就已经确定了。每个函数都会创建自己的作用域

区别:

1.作用域在定义时就确定了,全局执行上下文在全局作用域确定之后,js执行前创建,函数执行上下文是在调用函数时,函数代码执行前创建

2.作用域是静态的,定义好就不会再变化。执行上下文是动态的,调用函数时创建,调用结束上下文环境被释放。

3.上下文对象从属于作用域

 

作用域链

嵌套的作用域产生由内向外的过程。

比如:

输出c,先看当前作用域有没有,有输出,

看b当前作用域没有,去上一级外部看,有,输出

看a当前作用域没有,外部没有,最外部,有,输出

如果到最外层作用域都没有,就报错

 

闭包

闭包的代码:

function fn1(){

    var a=2;

    function fn2(){

          console.log(a);

     }

}

fn1();

-------这里fn2里有a的undefined值,这就是闭包

如何产生闭包?

当一个嵌套的内部函数引用了嵌套的外部函数数据(变量或函数)时,而且执行了外部函数,就产生了闭包

闭包是什么?

我们可以通过chorme调试方式来查看,

闭包就是存在于嵌套的内部函数中,包含的被引用变量的对象(即上例的a)

常见的闭包

1.将函数作为另一个函数的返回值

function fn1(){

   var a=2;-------执行到这的时候,已经有闭包了,因为有函数提升,fn2提到上面去了

   function fn2(){

   a++;

   console.log(a);

  }

  return fn2;

}

var f=fn1();----怎么看产生几个闭包?看调用了几次外部函数,这次产生一个闭包

f();---输出3---因为返回值是fn2,所以这里会直接进入a++这一行

f();---输出4

fn1();----产生的第二个闭包

2.将函数作为实参传给另一个函数调用

function show(msg,time){

  setTimeout(function(){

   alert(msg);----闭包产生是因为这里引用了msg

  },time)

}

show('marry',2000); 

闭包的作用

1.使函数内部变量在函数执行完后,仍然存活在内存中——延长了局部变量的生命周期

2.让函数外部能操作(读写)到函数内部的数据(变量、函数)

闭包的生命周期

闭包的产生:在嵌套内部函数定义执行完时产生

闭包的死亡:在嵌套内部函数称为垃圾对象时

闭包的应用:

定义JS模块

JS模块是:具有特定功能的JS文件,将所有的数据和功能封装到一个函数内部,只向外暴露一个包含n个方法的对象或函数

只需要通过模块暴露的对象调用方法来实现对应的功能。

方法一:不推荐

JS文件中:

function myModule(){

 var msg='hello world';

 function doSomething(){

   console.log('doSomething()'+msg.toUpperCase());

 }

 function doOtherthing(){

  console.log('doOtherthing()'+msg.toLowerCase());

 }

 return {

   doSomething:doSomething,

   doOtherthing:doOtherthing

  }

}

Script中

<script src="myModule.js" type="text/javascript" ></script>

<script>

    var module=myModule();

    module.doSomething();

   module.doOtherthing();

</script>

方法二:推荐

(function(){

  var msg='hello world';

 function doSomething(){

   console.log('doSomething()'+msg.toUpperCase());

 }

 function doOtherthing(){

  console.log('doOtherthing()'+msg.toLowerCase());

 }

 window.myModule2={

  doSomething:doSomething,

   doOtherthing:doOtherthing

  }

})()

 

<script src="myModule2.js" type="text/javascript" ></script>

<script>

     myModule2.doSomething();

     myModule2.doOtherthing();

</script>

闭包的缺点

1.函数执行完后,函数内的局部变量没有释放,占用内存时间会变长

2.容易造成内存泄漏

解决:

能不用闭包就不用

及时释放,让内部函数对象成为垃圾对象

 

内存溢出

程序运行出现的错误,当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误。

内存泄露

占用的内存没有及时释放,比如闭包、没有及时清理计时器或回调函数、全局变量

内存泄露多了就容易内存溢出了。

 

面向对象高级

对象创建模式

1.用Object构造函数创建(适用:起始的时候不确定对象内部的数据,         缺点:语句太多)

   var p=new Object();

   p.name='zyy';

   p.setName=function(name){

    this.name=name;

  }

2.字面量方式(适用:起始的时候确定对象内部的数据,         缺点:有重复代码)

 var p={

   name:'zyy',

   setName:function(name){

   this.name=name; 

  }

};

3.工厂模式(适用:需要创建多个对象,缺点:对象没有一个具体的类型,都是Object类型)

通过工厂函数动态创建对象

function createPerson(name,age){

   var obj={

     name:name,

     age:age,

     setName:function(){

      this.name=name;

    }

 }

  return obj;

}

var p=createPerson('zyy',18);

4.自定义构造函数,new的方式创建(适用:创建多个类型确定的对象,     缺点:每个对象都有相同的数据,浪费内存)

function Person(name,age){

   this.name=name;

   this.age=age;

   this.setName=function(name){

      this.name=name;

   }

}

var p=new Person('zyy',18);

5.构造函数+原型(适用:需要创建多个确定类型的对象)

自定义构造函数,属性在函数中初始化,方法添加到原型上、

function Person(name,age){

   this.name=name;

   this.age=age;

}

Person.prototype.setName=function(name){

  this.name=name;

}

var p=new Person('zyy',18);

原型链的继承

1.定义父类型的构造函数

2.给父类型的原型添加方法

3.定义子类型的构造函数

4.将子类型的原型prototype指向父类型的实例对象,父类型的实例能看到原型对象

5.将子类型原型的构造属性设置为子类型

6.给子类型的原型添加方法

7.创建子类型的对象:可以调用父类型的方法

关键是:子类型的原型为父类型的一个实例对象

function Father(){

   this.Fpro='Father property';

}

Father.prototype.showFprototype=function(){

  console.log(this.Fpro);

}

function Son(){

   this.Spro='Son property';

}

Son.prototype=new Father();

Sub.prototype.constructor=Sub;//让子类原型的构造函数指向子类型

Son.prototype.showSprototype=function(){

  console.log(this.Spro);

}

var son=new Son();

son.showFprototype();

 

组合继承

利用原型链实现对父类型对象的方法继承

用call()借用父类型构造函数初始化相同属性

function Person(name,age){

   this.name=name;

   this.age=age;

}

Person.prototype.setName=function(){

  this.name=name;

}

function Student(name,age,price){

  Person.call(this,name,age);

 this.price=price;

}

Student.prototype=new Person();

Student.prototype.constructor=Student;

Student.prototype.setPrice=function(price){

   this.price=price;

}

线程机制和事件机制

进程与线程

注意:

应用程序必须运行在某个进程的某个线程上

一个进程中至少有一个运行的线程:主线程,进程启动后自动创建

一个进程中也可以同时运行多个线程

一个进程内的数据可以供多个线程直接共享

多个进程之间的数据不能直接共享

线程池:保存多个线程对象的容器,实现线程对象的反复利用

问题

什么是多进程?什么是多线程

多进程:一个应用程序可以同时启动多个实例运行

多线程:在一个进程内,同时有多个线程运行

比较一下单线程和多线程运行,有什么区别?

多线程:

优点:提高CPU的利用率

缺点:创建多线程需要开销;线程间的切换需要开销;死锁和同步的问题

单线程:

优点:顺序执行

缺点:效率低

JS是单线程还是多线程?

JS是单线程运行的,但是H5中的Web Workers可以多线程运行

浏览器是单线程还是多线程?单进程还是多进程?

多线程;有的是单进程(火狐、老版IE),有的是多进程(chrome、新版IE)

浏览器内核

浏览器内核是浏览器运行的最核心的程序。

不同浏览器内核可能不一样,内核由很多模块组成。

内核由很多模块组成:

JS引擎模块:负责JS程序的编译与运行-----------主线程(js引擎也是程序,只是用来解析我们的代码,js引擎在浏览器内部)

html、css文档解析模块:负责页面文本的解析-----------主线程(读取文本,拆解)

DOM/CSS模块:负责dom/css在内存中的相关处理-----------主线程

布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)​​​​​​​-----------主线程

 

定时器模块:负责定时器的管理--------分线程

事件响应模块:负责事件的管理​​​​​​​--------分线程

网络请求模块:负责ajax请求​​​​​​​--------分线程

问题:

定时器真的是定时执行的吗?

不一定,一般会延迟一点,也可能延迟很长时间

定时器回调函数是在分线程执行的吗?

在主线程执行,JS是单线程的,

定时器怎么实现的?

事件循环模型,初始化代码(放到栈中)执行完才执行回调代码(放到回调队列)

 

Web Worker

html5提供了js分线程的实现,即web workers。我们可以将计算量大的代码交给Web Worker(分线程)运行而不冻结用户界面。

但是子线程完全受主线程控制,且不得操作DOM,所以这个新标准也没有改变js单线程的本质。

使用:

1.创建分线程执行的js文件

2.在主线程中的js中发消息并设置回调

var worker=new Worker("worker.js");---创建一个Worker对象并向它传递在新线程中执行的脚本URL

worker.onmessage=function(event){---接收worker传来的数据

   console.log(event.data);

}

worker.postMessage("hello world");-----向worker发送消息

案例:比如在文本框输入数字,点击按钮,弹出斐波那契数列的第几个数

<input type="text" placeholder="数值" id="number">

<button id="btn">计算</button>

<script>

      var input=document.getElementById('number');

      document.getElementById('btn').οnclick=function(){

        var number=input.value;

       var worker=new Worker("worker.js");

       worker.onmessage=function(event){

          console.log('主线程接收分线程返回的数据'+event.data);

          alert(event.data);

       }

      worker.postMessage(number);

      consolo.log('主线程向分线程发送数据'+number);

     }

</script>

worker.js文件中的代码:

function fibonacci(n){

  return n<=2?1:fibnonacci(n-1)+fibnonacci(n-2);

}

var onmessage=function(event){

     var num=event.data;

     console.log('分线程接收到主线程发送的数据' +num);

     var reslt=fibonacci(num);

     postMessage(result);

    console.log('分线程向主线程返回数据'+result);

}

相关API:

Worker:构造函数,加载分线程执行的js文件

Worker.prototype.onmessage:用于接收另一个线程的回调函数

Worker.prototype.postMessage:向另一个线程发送消息

缺点:

慢;不是每个浏览器都支持这个新特性;worker不能访问dom

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值