原生js 总结

数据类型

  • 基本数据类型有: string、Number、Boolean、Null、Undefined。这些类型分别在内存中占有固定的大小空间,它们的值保存在栈空间,我们通过按值来访问的。
  • 引用类型包括:Object、Array、Function等。 这些类型,则必须在堆内存中为这个值分配空间。由于引用类型值的大小不固定(对象有很多属性和方法,而且还可以动态的添加属性和方法),因此不能把他们保存到栈内存中。但内存地址大小是固定的,因此可以将内存地址保存在栈内存中。
  • 简而言之:栈内存中存放的是基本数据类型值,堆内存中存放的是引用类型值,引用类型值在内存中的地址存放在栈中,也就是我们常说的对象对象引用(指针)。

1

变量复制

<script type="text/javascript">
var a = 5;
var b = a;
console.log(a+"---"+b);//5---5
b = 6;//这里重新给b赋值,a值并没有改变
console.log(a+"---"+b);//5---6

var obj = {name:"lisi"};
var obj2 = obj;//这里是引用赋值,obj和obj2指向同一个对象
console.log(obj.name + "---" + obj2.name);//lisi---lisi
obj2.name = "wangwu";
console.log(obj.name + "---" + obj2.name);//wangwu---wangwu
</script>

由此可看出:在变量复制方面,基本类型和引用类型也有所不同,基本类型复制的是值本身,而引用类型复制的是
内存地址。

常用方法

JS选取DOM元素的方法

注意:原生JS选取DOM元素比使用jQuery类库选取要快很多

  • 通过 ID 选取元素: document.getElementById('myid');
  • 通过CLASS选取元素 : document.getElementsByClassName('myclass')[0];
  • 通过标签选取元素: document.getElementsByTagName('mydiv')[0];
  • 通过NAME属性选取元素(常用于表单): document.getElementsByName('myname')[0];

JS修改CSS样式

document.getElementById('myid').style.display = 'none';

JS修改CLASS属性

document.getElementById('myid').className = 'active';
如果有多个CLASS属性,即用空格隔开
document.getElementById('myid').className = 'active div-1';
移除该元素上的所有CLASS
document.getElementById('myid').className = '';

注意:使用classList会优于使用className

document.getElementById('myid').classList.item(0);//item为类名的索引

document.getElementById('myid').classList.length;//只读属性

document.getElementById('myid').classList.add('newClass');//添加class

document.getElementById('myid').classList.remove('newClass');//移除class

document.getElementById('myid').classList.toggle('newClass');//切换,有则移除,没有则添加

document.getElementById('myid').classList.contains('newClass');//判断是否存在该class

补充:addremove方法不允许链式操作,因为返回的都是undefined,其次,也不允许同时添加或删除多个class,可以自行扩展一下

JS修改文本

document.getElementById('myid').innerHTML = '123';

JS创建元素并向其中追加文本

var newdiv = document.createElement('div');
var newtext = document.createTextNode('123');
newdiv.appendChild(newtext);
document.body.appendChild(newdiv);
同理:removeChild()移除节点,并返回节点
cloneNode()复制节点
insertBefore()插入节点(父节点内容的最前面)

注意:insertBefore()有两个参数,第一个是插入的节点,第二个是插入的位置

例子:

var list = document.getElementById('myList');

list.insertBefore(newItem,list.childNodes[1]);

//插入新节点newItem到list的第二个子节点

JS返回所有子节点对象childNodes

  • firstChild返回第一个子节点
  • lastChild返回最后一个子节点
  • parentNode返回父节点对象
  • nextSibling返回下一个兄弟节点对象
  • previousSibling返回前一个兄弟节点对象
  • nodeName返回节点的HTML标记名称
var mylist = document.getElementById('myid');
for(var i=0,i<mylist.childNodes.length;i++){
console.log(mylist.childNodes[i]);
}

浏览器对象模型

  • 全局变量是 window 对象的属性
  • 全局函数是 window 对象的方法
  • window.open() 打开新窗口 浏览器会自动在顶部创建窗口,不过如果指定的窗口已经存在,则不会自动使那个窗口可见。
  • window.close() 关闭当前窗口
  • window.moveTo()、moveBy() 移动当前窗口
  • window.resizeTo()、resizeBy() 调整当前窗口的尺寸
  • window.scrollTo()、scrollBy() 调整窗口滚动条的偏移位置的两个函数
  • Window.focus()、blur() 控制窗口的显示焦点 调用focus()方法会请求系统将键盘焦点赋予窗口,会把窗口移到堆栈顺序的顶部,使窗口可见,调用blur()则会放弃键盘焦点。
  • Window 尺寸:
    • 对于Internet Explorer、Chrome、Firefox、Opera 以及 Safari:
      • window.innerHeight - 浏览器窗口的内部高度
      • window.innerWidth - 浏览器窗口的内部宽度
    • 对于 Internet Explorer 8、7、6、5:
      • document.documentElement.clientHeight
      • document.documentElement.clientWidth
    • 或者
      • document.body.clientHeight
      • document.body.clientWidth
  • window.screen 对象包含有关用户屏幕的信息
    • screen.availWidth - 可用的屏幕宽度
    • screen.availHeight - 可用的屏幕高度
      • height:显示Web浏览器的屏幕高度
    • width:显示Web浏览器的屏幕宽度
    • availLeft:屏幕最左侧的x坐标
    • availTop:屏幕最顶部的y坐标
    • colorDepth:浏览器分配的颜色数或者颜色深度
      • pixelDepth:显示浏览器的屏幕的颜色深度
  • window.location 对象用于获得当前页面的地址 (URL),并把浏览器重定向到新的页面。
    • location.hostname 返回 web 主机的域名
    • location.pathname 返回当前页面的路径和文件名
    • location.port 返回 web 主机的端口 (80 或 443)
    • location.protocol 返回所使用的 web 协议(http:// 或 https://)
  • window.history 对象包含浏览器的历史。
    • history.back() - 与在浏览器点击后退按钮相同
    • history.forward() - 与在浏览器中点击按钮向前相同
  • window.navigator 对象包含有关访问者浏览器的信息。
// window.navigator 对象在编写时可不使用 window 这个前缀。
<div id="example"></div>

<script>

txt = "<p>Browser CodeName: " + navigator.appCodeName + "</p>";
txt+= "<p>Browser Name: " + navigator.appName + "</p>";
txt+= "<p>Browser Version: " + navigator.appVersion + "</p>";
txt+= "<p>Cookies Enabled: " + navigator.cookieEnabled + "</p>";
txt+= "<p>Platform: " + navigator.platform + "</p>";
txt+= "<p>User-agent header: " + navigator.userAgent + "</p>";
txt+= "<p>User-agent language: " + navigator.systemLanguage + "</p>";

document.getElementById("example").innerHTML=txt;

</script>
/* 警告:来自 navigator 对象的信息具有误导性,不应该被用于检测浏览器版本,这是因为:
    navigator 数据可被浏览器使用者更改
    浏览器无法报告晚于浏览器发布的新操作系统
*/

函数 this 的工作原理(5种情况)

  1. 全局作用域内: 它将会指向全局对象,即window对象
  2. 函数调用:在局部作用域内调用函数时,this 指向全局对象
  3. 方法调用: this 指向调用该方法的对象。
  4. 调用构造函数: 在构造函数内部,this 指向新创建的对象。
  5. 显式的设置this指向: 当使用call 或者 apply 方法时,函数内的 this 将会被显式设置为函数调用的第一个参数。

匿名函数

在javascript里任何匿名函数都是属于window对象。在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了(可以通过在这个变量后面加一对圆括号来调用这个匿名函数),因为匿名函数是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象。

<script type="text/javascript">
(function(){
    console.log(this == window);//true
})();
</script>

参数传递

  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
  • 按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。
  • 按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG
<script type="text/javascript">
     function test(num){//按值传递
         num+=5;
         console.log(num);
         return num;
     }
     var num = 5;
     var result = test(num);
     console.log(result);// 10 如果是按引用传递,那么函数里的num会成为类似全局变量,把外面的num覆盖掉
     console.log(num);// 5 也就是说,最后应该输出20(这里输出10)
</script>

JS的基本类型,是按值传递的。如果是引用传递的话,那么函数内的变量将是全局变量,在外部也可以访问,但这明显是不可能的。
再看一个例子:

<script type="text/javascript">
function setName(obj){//obj = person
    obj.name = "lisi";
    obj = new Object();
    obj.name = "wangwu";
}
var person = new Object();
setName(person);
alert(person.name);
console.log(obj.name);//Uncaught ReferenceError: obj is not defined
</script>
  • 在将person传递给obj后,其name属性就被设置成了”lisi”。又将obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。
  • 如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为”wangwu”的新对象,但事实上并没有,其name属性依然是”lisi”
  • 这就说明:即使在函数内部修改了参数的值,但原始的引用仍然保持不变。实际上,当在函数内重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。

执行环境

  • 执行环境定义了变量函数有权访问其他数据,可以分为全局执行环境和局部执行环境。
  • 全局执行环境最外层的执行环境,在浏览器中,全局执行环境是window对象,因此,所有的全局变量的函数都是作为window的属性和方法创建的。
  • 注意:对于局部执行环境,其内部的代码执行完毕后,该环境将被销毁,其中的变量和函数也随之销毁,如果是全局执行环境,需所有程序执行完毕或网页完毕后才会销毁。
  • 内部的环境可以通过作用域链访问所有的外部环境,但外部的环境无法访问内部的环境中的任何变量和函数。每个环境都可以向上搜索作用域链,以查询变量和函数名;但是任何环境都不能通过向下搜索作用域而进入另一个执行环境。

作用域

当代码在一个环境中执行的时候,就会形成作用域链,它的作用是保证对执行环境中有访问权限的变量和函数进行有序访问(内部深层环境可以访问外层环境,反之不成立),作用域链的前端,就是执行环境的变量对象。
与执行环境非常相似。列如:

<script type="text/javascript">
      var name = "wangwu"; //全局变量,会覆盖全局中的name
      function setName(){
          name = "lisi";
      }
      setName();
      console.log(name);//lisi
</script>
<script type="text/javascript">
      var name = "wangwu";
      function setName(name){
          var functionName = name; //局部变量, 外部不能访问
          console.log(name);
      }
      setName("lisi");//lisi
      console.log(name);//wangwu
      console.log(functionName );// Uncaught ReferenceError: functionName is not defined
</script>

内存泄漏常见情况

javascript具有自动垃圾回收机制,一旦数据不再使用,可以将其设为”null”来释放引用。

循环引用

一个经典的例子:一个DOM对象被一个Javascript对象引用,与此同时又引用同一个或其它的Javascript对象,这个DOM对象可能会引发内存泄露。这个DOM对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用DOM元素的对象或DOM对象的引用需要被赋值为null。

<body>
<script type="text/javascript">
var oBox = document.getElementById("box");
var obj = {};
oBox.name = obj;
obj.age = oBox;//这样就发生了循环引用
</script>
</body>

DOM泄漏

当原有的DOM被移除时,子结点引用没有被移除则无法回收。

var select = document.querySelector;
var treeRef = select('#tree');
//在COM树中leafRef是treeFre的一个子结点
var leafRef = select('#leaf'); 
var body = select('body');
body.removeChild(treeRef);
//#tree不能被回收入,因为treeRef还在
//解决方法:
treeRef = null;
//tree还不能被回收,因为叶子结果leafRef还在
leafRef = null;
//现在#tree可以被释放了。

定时器泄漏

for (var i = 0; i < 1000; i++) {
  var obj = {
    fn: function() {
      var that = this;
      var val = setTimeout(function() {
        that.fn();
      }, 500);
    }
  }
  obj.fn();
  //虽然你想回收但是timer还在
  obj = null;
}

闭包(常见)

通过闭包引用其包含函数的局部变量,当闭包结束后该局部变量无法被垃圾回收机制回收,造成内存泄漏。

垃圾回收

标记清除

  • js中最常用的垃圾回收方式就是标记清除。垃圾收集器在运行的时候回给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,消除那些带标记的值并回收它们所占用的内存空间。
  • 例如:在函数中声明一个变量,就将这个变量标记为”进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为”离开环境”。
function test(){
 var x = 10 ; //被标记 ,进入环境 
 var y = 20 ; //被标记 ,进入环境
}
test(); //执行完毕后 x、y又被标离开环境,被回收。

引用计数

跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存

function test(){
 var x = {} ; //x的引用次数为0 
 var y = x ; //x的引用次数加1,为1 
 var z =x; //x的引用次数再加1,为2
 var y ={}; //x的引用次数减1,为1
}
  • 为了避免类似这样的循环引用问题,最好在不使用它们的时候手工断开原生js对象与DOM元素之间的连接。将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。
  • IE9把BOM和DOM对象都转换成真正的js对象。这样,就避免了两种垃圾收集算法并存导致的问题,也消除了常见的内存泄漏现象。

猜你喜欢

转载自blog.csdn.net/xustart7720/article/details/79385340
今日推荐