一些你不知道的js特性【一】

关于js

  我们知道完整的js包括三个方面ECMAScript、DOM(文档对象模型)、BOM(浏览器对象模型)。

  ECMAScript定义了与宿主无关的预言基础,比如:语法(包含正则语法)、类型、语句、关键字、保留字、操作符、对象

  DOM是针对HTML(基于XML但经过拓展)的api。主成部分主要有DOM Core(映射基于XML的文档结构)、DOM HTML(拓展DOM Core,增加的针对HTML的对象和方法)、DOM视图(跟踪不同文档视图的接口)、DOM事件、DOM样式(基于css为元素应用样式的接口)、DOM遍历和范围、DOM加载和保存、DOM验证等

  BOM是访问和操作浏览器窗口的api。一般来说针对浏览器的js拓展都算是BOM的一部分,比如:弹出新窗口的功能、关闭窗口功能、访问浏览器属性等。比较典型的就是window对象、navigator对象、location对象。其中window对象比较特殊,他及时BOM的核心,也是js的全局对象。

 

关于变量

  1.不推荐修改变量所保存值的类型

  2.使用操作符var定义的变量是该变量作用域下的局部变量。而不使用var定义的变量是全局变量。如

function a(){
  var t = 1;
}
function b(){
  m = 2;
}
a();
alert(t);//报错
b();
alert(m)//2

  但是推荐使用var或es6语法let/const定义变量,且局部作用域中定义全局变量很难维护。

  3.非对象的变量比较使用"===",这样比较不会去转换。使用"=="比较,如果其中有字符串、数字的比较可能涉及到转换成数字或字符串。

  4.undefined表示变量未赋值,null表示是一个空对象指针。使用"=="比较两者会是true,undefined派生至null,比较的时候会转换起操作数。给将来要保存对象的变量赋值null是一种好的实践,也进一步区分null和undefined。

  5.在进行算数计算时,所有的八进制和十六进制都转换成十进制计算,结果也是十进制。

  6.es5中没有块级的作用域,如

if(1){
    var tt = 1;
}
alert(tt);//1

  es6新增了块级作用域,使用let/const定义。

  7.某个作用域下的直接变量(包括函数和普通变量)都会在解析该作用域时首先声明,如

function m(){alert(a); var a = 2;}
m();//“undefind”,变量a在函数m解析时最开始就声明(不过赋值需要按代码顺序执行到相应位置才赋值)
function s(){alert(a); if(1){var a = 2;}}
s();//“undefined”,参考第6项。
function n(){alert(b);}
n();//浏览器报错,变量b没有声明

   8.强烈建议不要实例化数据类型,特别是基本类型对应的对象实例;

var t = new Number('1');//不推荐使用
typeof t;//"object"
var m = Number('1');//推荐使用
typeof m;//"number"
var q = 1;//如果能直接定义变量值,就没必要使用Number多此一举了
typeof q;//"number"

 

 

NaN

  任何有NaN参与的计算结果是NaN;NaN与任何值都不相等,包括他自己。

 

valueOf和toString

  JS数据类型都拥有valueOf和toString这两个方法.

  valueOf用于返回指定对象的原始值。对于number/string/boolean/object/array/function不用说,就是返回其本身。date类型会返回其毫秒时间数值。

  toString将指定元素转换成字符串。但是其中Date/Array/Object/Function有些不同。

var num = 1,
    str = 'chua',
    bol = true,
    fun = function(){alert(1)},
    //und = undefined,nul = null,//这两个没有toString和valueOf属性
    obj = {},
    arr = [1,2,3],
    dat = new Date();

dat.toString();//"Thu Jul 21 2016 20:37:34 GMT+0800 (中国标准时间)"
arr.toString();//"1,2,3"
obj.toString();//"[object Object]"
fun.toString();//"function (){alert(1)}"
bol.toString();//"true"

   当然其中有些不一样的对象,比如 new Number(1);new String(1);new Boolean(1),这些特殊的对象使用toString得到的结果会和1/'1'/true使用toString的结果相同

 

运算符+和运算符-  

   需要特别注意的是:数字与字符串相加时数字会转换成字符串相加;数字与字符串相减时,字符串会转换成数字再相减;

 

比较运算符

  比较运算符比较字符串时比较两个字符串对应位置的字符所对应的编码大小。所以大写字母始终是小于任何一个小写字母。

  所以要想得到我们想要的结果("B" > "a")那么需要将两者都转化为小写形式。

  而且注意"123" > "23"结果是false,因为这两个都是字符串,比较对应位置的字符就得到这样的结果。如果有任何一个是数字那就不会出现问题

  "a" < 3返回false,因为"a"转换成了NaN;"a" >= 3也返回false

  "=="比较会转换类型,"==="不会转换类型。对象比较"=="需要是引用同一个对象才会返回true

 

关于delete

  现代浏览器(IE9+)全局变量不能删除,全局属性可以删除

 

关于页面加载渲染过程

  无论是css还是js都是并行下载的,我们看chrome的network时间就能明白(时间重叠)

  资料:网页加载历程详解http://www.educity.cn/wenda/143254.html

 

获取iframe的window对象

<body>
  <iframe id="frame1" name="frame2" class="frame1" frameborder="0" scrolling="no" width="500px" height="500px" src="v_components.html"></iframe>
  <iframe id="frame2" name="frame3" class="frame2" frameborder="0" scrolling="no" width="400px" height="400px" src="v_components.html"></iframe>
</body>

1.通过css查询节点: $('#frame1')[0].contentWindow;$('#frame2')[0].contentWindow;

2.通过window.frames[0]

 

网页的锚部分location.hash

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。

#代表网页中的一个位置,比如

http://www.cnblogs.com/chuaWeb/p/gulp.html#!comments

就代表网页的!comments位置。浏览器读取这个URL后,会自动滚动到标签的name="!comments"或id="!comments"的位置

单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页。如

http://www.cnblogs.com/chuaWeb/p/gulp.html#blog-comments-placeholder
//修改
http://www.cnblogs.com/chuaWeb/p/gulp.html#!comments

更改url的属性如location.(href/hash/search/host/hostname/port/pathname)都会根据新值来改变url

改变#会改变浏览器的访问历史

Google还规定,如果你希望Ajax生成的内容被浏览引擎读取,那么URL中可以使用"#!",Google会自动将其后面的内容转成查询字符串_escaped_fragment_的值。

比如,Google发现新版twitter的URL如下:

http://twitter.com/#!/username

等价于

http://twitter.com/?_escaped_fragment_=/username

通过这种机制,Google就可以索引动态的Ajax内容。

 location.replace(url)方法会替换当前的url,但是不会产生浏览记录(用户不能回到前一个页面了)

 

 

setTimeout/setInterval

请不要给这两个函数的第一个参数传递字符串,这样会导致性能损失,传递函数最好。

延时调用的代码都是在全局作用域下执行的,因此函数中的this非严格模式下指的是window,在严格模式下是undefined。

使用延时调用来模拟间歇调用(setInterval)是一种最佳方式.因为在前一个间歇调用排队调用的时候(因为前面有一个需要消耗大量时间的计算还没有执行完)后一个间歇调用也进入调用排队了,结果就是两个间歇调用连续执行。

 

alert/comfirm/prompt

浏览器对话框都是同步执行的,也就是说弹出对话框的时候代码会停止执行,关闭对话框后代码会恢复执行。

 

innerHTML

innerHTML中插入script节点是不会执行的(IE8-是唯一能运行里面脚本的浏览器,且必须满足必须为<script>元素指定defer 属性,并且<script>元素必须位于(微软所谓的)“有作用域的元素”(scoped element)之后。)

document.getElementById('form').innerHTML = '<script defer="defer" >alert(1)</script>';

 innerHTML中插入style节点在IE9+浏览其中会起作用但是在IE8-中不起作用

 

label

label语句可以在代码中添加标签,以便将来使用。通常都与for一起使用用来跳出多层循环(警告:尽量避免过多的循环层)

var num = 0;
    start:    for (var i = 0 ; i < 10 ; i++){
         for (var j = 0 ; j < 10 ; j++){
              if( i == 5 && j == 5 ){
                    break start;
              }
         num++;
         }
    }
    alert(num); // 循环在 i 为5,j 为5的时候跳出双循环,返回到outPoint层继续执行,输出 55

 

 

switch【避免使用,参考严格模式】

相比其他语言他要灵活很多,case后面可以是表达式,只要这个表达式的值能和switch中传递的值全等就会进入这个分支

var num = 25;
switch(true){
    case num == '25': 
        alert(0);
        break;
    case num < 25: 
        alert(1);
        break;
    detault:
        alert(2);
}

很明显,ture和num == '25'值全等,所以弹出“0”

 

arguments

函数中可以通过arguments获取参数,可以修改arguments中的值(会同步到对应的传参)

function a(tt,mm){arguments[1] = 10;alert(mm)}
a(1);//'undefined',因为根本就没有传递第二个参数
a(1,2);//10,第二个参数被修改,同步mm

 js中的参数都是值传递,不可能引用传值的情况。对象可以看做一个引用类型,同一个对象只有一份数据空间,如果传递对象的话,在函数内部修改对象实际就改变了这个唯一的一份数据空间

var a = {name: 'chua'}
function tt(b){
    b.name = 'yyqing';
    alert(a.name);
}
tt(a);//'yyqing',对象a只有一份空间,函数tt中b和a指向同一份空间
function a(){};
a.tname = 'chua';
function tt(c){
    c.tname = 'yyqing';
    alert(a.tname);
}
tt(a);//'yyqing',对象a只有一份空间,函数tt中b和a指向同一份空间

 

arguments.callee和function.caller【避免使用,参考严格模式】

function a(){
  alert(arguments.callee);
}
a();/*function a(){
  alert(arguments.callee);
}*/
function a(){
 b();
}
function b(){
 alert(b.caller);
}
a();/*function a(){
 b();
}
*/

 

 

Array

数组的length属性并非只读属性,可以为length属性赋值以添加或减少数组的长度。

数组的sort方法排序,接收一个比较函数,如果使第一项在第二项前面,则返回负数;使第一项在第二项后面,则返回正数;

var tar = [1,8,2,4,10];
function compare(v1,v2){
  if(v1 < v2){
    return -1;
  }else if(v1 > v2){
    return 1;
  }else{
    return 0;
  }
}
tar.sort(compare); //[1, 2, 4, 8, 10]

concat方法

var cor = [1,2,3];
cor.concat(4,[5,6]);//[1, 2, 3, 4, 5, 6]
cor.concat(4,[[5,6]]);//[1, 2, 3, 4, Array[2]]

数组的5个遍历方法every/filter/forEach/map/some

数组的reduce和reduceRight迭代所有的项,然后构建一个最终返回值

 

 

this

最外层this是window对象,函数作用域中this是调用这个函数的那个对象(或者说作用域)

var t = 1;
function a(){
  this.m = 2;
  alert(this.t);
}
a();//1
alert(m);//2;

如上,函数a属于window,所以a中的this指的是window对象。

var v = {
 o: function(){
    this.p = 2;
    alert(this.p);
  }
}
v.o();//2
alert(v.p);//2

上面这个例子中o函数中的this指的是v对象。

 箭头函数中this指向函数定义时的this。

 

Function.prototype.bind

作用是创建一个函数实例,并将参数作为这个实例的this使用。

function a(){
 alert(this.b);
}
var t = {b:1};
var c = a.bind(t);
c();

这个比使用self等保存this更加专业

var myObj = {
    tname: 'chua',
    getAsyncData: function(fn){
        fn();
    },
    render: function () {
        var self = this;
        this.getAsyncData(function () {
            console.log(self.tname);
        });
    }
};

myObj.render();//'chua'

可以改成

var myObj = {
    tname: 'chua',
    getAsyncData: function(fn){
        fn();
    },
    render: function () {
        this.getAsyncData(function () {
            console.log(this.tname);
        }.bind(this));
    }
};

myObj.render();//'chua'

但是需要注意的是,IE9+才支持该方法。 

 

文件下载

正常情况下使用

<a href='xxx.png'>download</a>

点击标签正常情况下是展示该图片,而非弹出下载提示。可以通过后台设置协议头Content-Disposition属性为attachment来强制资源作为附件下载。

 

读取和设置文本域光标位置

/**
 * 获得光标位置兼容IE/FF
 * 
 例:
    var obj = document.getElementById("tx1");
        var pos = getCaretPosition(obj);
        alert("--"+pos);
 */
function getCaretPosition(obj) {
    var result = 0;
    if (obj.selectionStart) { //IE以外 
        result = obj.selectionStart;
    } else { //IE 
        try{
            var rng;
            if (obj.tagName == "textarea") { //TEXTAREA 
                rng = event.srcElement.createTextRange();
                rng.moveToPoint(event.x, event.y);
            } else { //Text 
                rng = document.selection.createRange();
            }
            rng.moveStart("character", -event.srcElement.value.length);
            result = rng.text.length;
        }catch (e){
            throw new Error(10,"asdasdasd")
        } 
    }
    return result;
}
/**
 * 设置光标位置兼容IE/FF
 * @param tObj
 * @param sPos
 * 
 例:
  var obj =document.getElementById("tx1");  
        var sPos = obj.value.length-1;  
        setCaretPosition(obj, sPos);  
 */
function setCaretPosition(tObj, sPos){  
    if(tObj && sPos){
        if(tObj.setSelectionRange){  
            setTimeout(function(){  
                tObj.setSelectionRange(sPos, sPos);  
                tObj.focus();  
            }, 0);  
        }else if(tObj.createTextRange){  
            var rng = tObj.createTextRange();  
            rng.move('character', sPos);  
            rng.select();  
        }  
    }
}
View Code

 

JSON

JSON对象中属性是函数的会被忽略情况,比如

var obj = {
  fn: function(){},
  name: 'chua'
};
JSON.stringify(obj);//"{"name":"chua"}"

 

 

 

=======================================================

后记:

2019.12.20 笔记大部分是很久很久之前整理的,主要涉及es5,es6的后续开篇

 

posted @ 2019-12-20 20:30  chua1989  阅读(512)  评论(0编辑  收藏  举报