JavaScript的发现与理解

1. 用var操作符声明的变量与省略var操作符声明的变量的区别

在JavaScript中,是通过var关键字来声明变量的(称这种声明方式为正规声明),但是,直接给没有用var 声明过的标识符赋值,也会创建变量(称这种声明方式为非正规声明),但是,它们是有区别的,区别如下:

  1. 非正规变量只有在标识符被赋值的时候才会创建,而正规声明的变量,在声明时创建;
  2. 正规声明的变量在其作用域内的任何地方都可用,哪怕是其声明之前,只不过在其声明之前,它的值是undefined,这是因为声明提前的缘故。而非正规声明的变量,只能在其创建之后的有效作用域内使用;
  3. 非正规声明的变量总是全局变量,即使它在函数内创建也是如此;
  4. 在函数内,全局变量中有同名的变量,正规声明的标识符会新创建变量并且使用优先级高于全局变量,而对于非正规声明的变量不会重新创建变量,只是使用同名的全局变量,即并非正规声明无效;
  5. 正规变量无法通过delete删除,而非正规变量可以;

2. 省略var操作符声明的变量是全局变量的原因

我认为ECMAScript这样规定的原因是:
假设某个地方对全局变量variable进行赋值;
当因为某种失误variable没有被声明或创建时,解析器有两种方案:

  1. 作为错误处理;
  2. 创建variable变量;

由于JavaScritpt是作为浏览器的脚本语言,且浏览器的JavaScritpt代码是可以分散的,且浏览器并不能保证所有的JavaScritpt代码全部执行,也不能保证所有JavaScript代码按顺序执行,所以经常存在某个声明变量的语句未在合适的时机执行的情况,所以把这种示声明的变量的引用当作错误处理是不合适的,所以第一种方案不选,只能选择第2种方案:创建变量;

创建变量也有两种方案:

  1. 创建局部变量;
  2. 创建全局变量;

但考虑以下2个原因,应该选择:创建全局变量;

  1. 如果创建局部变量,则当其它JavaScritpt代码片断执行时仍存在上述问题;但如果创建全局变量,则当其它JavaScritpt代码片断执行时便不存在该变量没有声明的情况了;
  2. 上述问题发生在引用其它JavaScript代码片断中声明的全局变量的情况下,所以,如果创建局部变量的话,就违背的了代码的意图(把该变量作为全局变量用);

所以,综合以上就有:省略var操作符声明的变量是全局变量;

3. 作用域链的理解

  1. 可以把作用域链理解成是一个栈结构;
  2. 每个作用域都有一个作用域对象用于保存在该作用域内创建的变量(包括函数),其保存的方式是:在作用域内创建的变量会成为作用域对象的属性;
  3. 作用链链保存的是各级作用域对象的引用,其中最近的作用域的作用域对象在最前端,越远的作用域的作用域对象越靠后;
  4. 全局作用域的作用域对象是全局对象本身;所以,每个作用域链的最后端都是全局对象的引用;
  5. 在全局作用域内创建的变量会成为全局对象的属性的原因:由于2(在作用域内创建的变量会成为作用域对象的属性)和4(全局作用域的作用域对象是全局对象本身),所以在全局作用域创建的变量会成为全局对象的属性;
  6. 函数的作用域链是在函数对象被创建时(被定义时)创建的;
  7. 每当函数被执行时,都会新创建一个函数的作用域对象,并把该作用域对象推到作用域链的最前端;
  8. 每当函数执行结束时,都会把函数的作用域对象从该函数作用链中推出;

4. 标识符查找

  • 访问属性,查找的是原型链;
  • 访问变量,查找的是作用域链;

作用链是一个栈,栈里保存的是作用域对象,栈的最顶端是当前作用域对象,栈的最底端是全局作用域对象;
作用域对象只有2种:

  • 全局作用域对象;
  • 函数作用域对象;

所以当通过属性调用的方式调用对象的方法时,该对象只会成this的值;不会成为它的方法(即:对象中属性值是函数的属性)的作用域链中的作用域对象,不过可以通过with操作符使对象成为它的方法的作用域链中的作用域对象;

5. 数组的本质

数组的本质还是对象,数组是通过对象实现的,它并没有为索引加入新的机制,数组中的索引本质是作为数组对象的属性存在的,因为打点调用的属性不能是数字,所以数组的索引不通过打点调用,只能通过方括号的方式调用;

6. 函数this的取值机制

经过测试,总结得出以下结论:

结论1:容易理解的this取值机制

函数的this的值取决于函数的调用方式;
函数有2种调用方式,如下:

  • 直接调用;如:fn();
  • 属性调用;如:obj.fn();
  • 当函数以直接调用的方式调用时,this的值为全局对象,如:winodw;
  • 当函数以属性调用的方式调用时,this的值为调用函数的对象;

对象属性调用有2种方式:

  • 打点调用,如:obj.attr
  • 方括号调用,如:obj["attr"]

所有通过这2种属性调用方式调用的函数的this的值都为调用函数的对象;

又因为数组中的索引本质是作为数组对象的属性存在的,所以调用数组中的函数(如:array[index]();)时,被调用的函数的this的值为该数组对象;

结论2:this取值的本质机制

我认为,本质上,函数的this的值取决于函数对象的查找方式:

  • 如果函数对象在被调用时是在作用域链上查找到的,则该函数中this的值为全局对象;
  • 如果函数对象在被调用时是在原型链上查找到的,则该函数中this的值为调用函数的对象;

因为:

  • 当函数以 直接调用 的方式调用时,它是在作用域链上被查找到的,所以该函数中this的值为全局对象;
  • 当函数以 属性调用 的方式调用时,它是在原型链上被查找到的,所以该函数中this的值为调用函数的对象;

所以:

结论1是正确的,并且结论1只是结论2的一个特例,即:结论2包含结论1;

注意

  • 计算属性的get和set函数中的this也符合上述规则;
  • ES6引用了模块机制,对模块中顶层的this做了处理,使得在ES6的模块中,顶层的this的值是undefined,所以,不应该在顶层代码使用this; 所以,如果在模块中直接访问全局对象(如 window 对象)的计算属性(即:不是通过调用 window 属性的方式来访问,如 window.计算属性名,而是直接访问,如 全局对象的计算属性名),则该计算属性的 set 和 get 函数的 this 也是 undefined ;

7. 函数调用表达的理解

假设在全局环境中有以下代码:

var inWhere = "The Window";
var aObject = {
        inWhere:"The aObject",
        getWhere:function(){
              return this.inWhere
           }
  };

则在全局环境下,以下三句代码及其执行结果如下:
代码1:

aObject.getWhere();     //结果:"The aObject"

代码2:

(aObject.getWhere)();       //结果:"The aObject"

代码3:

(aObject.getWhere=aObject.getWhere)();  //结果:"The Window" 

我认为造成以上执行结果的原因是:

  • 因为对于代码1和代码2中的函数在被调用时,都是在原型链上查找到的,所以它们的执行结果均为The aObject
  • 因为赋值语句=在内部本质上是作为函数来实现的,并且这个函数有个返回值,会返回等号=右边表达式的值,在代码3中该返回值就是getWhere函数,所以 (aObject.getWhere=aObject.getWhere) 结果是getWhere函数,并且这个函数是从等号=函数的返回值中取得的;又因为返回值是在作用域链中查找到的,所以在getWhere被执行时,this的值为全局对象,所以代码3执行结果是The Window

8. 未定义的变量与未定义的属性的访问

假设以下代码在全局作用域下执行,且gby和dyx都从未定义过,则执行结果如下:

gby;        //结果:报错:ReferenceError: Can't find variable: gby;
window.gby; //结果:undefined
window.dyx; //结果:undefined

所以:访问未定义的变量会报错,但访问未定义的属性不会报错,只是结果为undefined;
如果gby是全局变量,则gby 和 window.gby 访问的都是同一个变量,那为什么这两种访问方式的结果不一样呢?我认为原因如下:
访问变量时是通过搜索作用域链来查找变量的,而访问属性是通过搜索原型链来查找属性的,因为这两种访问方式的搜索方式不一样,所以导致了结果不一样;

9. JavaStript的特点

  1. JavaStript解析器会忽略后面对已存的变量的声明操作,但会执行对已存的变量的赋值操作(包括初始化操作);
  2. 当function关键字用作函数的声明时,不能省略函数名字,当function关键字用在表达式中创建函数时,就可以省略函数名;可以通过圆括号将“()”将函数声明转成函数表达式;所以直接执行代码:function(){}(); 会报错,而代码:(function(){})();可以正常执行;
  3. 声明的全局变量会成为全局对象(window对象)的属性,但定义全局变量 和 直接在全局对象上定义属性 是有差别的,差别如下:
    3.1. 定义全局变量生成的属性的configurable特性配置 false
    3.2直接在全局对象上定义属性的属性的configurable特性配置 true
    所以,通过定义变量生成的属性不能用delete操作符删除,通过直接定义属性的属性可能用delete操作符删除;

10. new操作符的特性

使用new操作符的格式如下:

new 构造函数;

经过探究,new操作符有如下特性:

  1. 每次调用都会创建一个新的对象;
  2. 会把构造函数的原型属性设置成新创建的对象的原型;
  3. 会把构造函数的this指向新创建的对象;
  4. 如果构造函数没有返回值,则会默认返回new新创建的对象;
  5. 如果构造函数有返回值,则会返回构造函数返回的值;

11. new.target

new.target属性是一个可以被所有函数访问的元属性
new.target属性允许你检测函数或构造方法是否通过是通过new运算符被调用的。
new.target的具体取值如下:

  1. 在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用;
  2. 在普通的函数调用中,new.target 的值是undefined。
  3. 在箭头函数中,new.target指向外围函数的 new.target。

new.target语法由一个关键字"new",一个点,和一个属性名"target"组成。通常标识符.的作用是提供属性访问的上下文,但这里new.其实不是一个真正的对象。不过在构造方法调用中,new.target指向被new调用的构造函数,所以new.成为了一个虚拟上下文。

12. undefined和null

在JavaScript的原始类型中包含了undefined和null;其中undefined是全局对象的一个属性,null是一个字面量;

13. 正则表达式

在JavaScript中创建正则表达式对象有两种方式:

  1. 正则表达式字面量;
  2. RegExp构造函数,格式如下:new RegExp(模式:string,标志:string);

通过这2种方式创建的正则表达式是有区别的,如下:
对于相同的正则表达式字面量,共享峡一个RegExp类型的实例,而使用构造函数创建的每一个RegExp类型的实例都是一个新实例;

注意:
RegExp构造函数接收2个参数,第1个参数是要匹配的字符串模式,第2个参数是可选的标志字符串;在字符串格式的模式中,需要对元字符进行双重转义;

14. 提升

JavaScript在解析JavaScript代码之前,会先进行一个声明提升的预处理,声明提升的规则如下:

  1. 会把声明提升到单个<script>标签内的每个作用域内的最顶端 或者 单个文件内的每个作用域内的最顶端;
  2. 提升后,变量声明总是放在所有函数声明的前面;
  3. 对于同一作用域内重复声明的变量或者函数,有如下规则:
    3.1 对于变量,解析器会忽略第1次声明之后的同名变量的声明;
    3.2 对于函数,后面声明的函数会覆盖前面声明的同名函数;
  4. 变量声明的提升只是提升变量名字的声明,不会提升变量的赋值操作;对于函数声明的提升,不仅会提升函数名字的导明,也会提升函数的代码定义;
  5. FireFox浏览器不能对代码块内的声明进行提升;

注意:
由于上述的规则2,所以在同一声明提升的范围内,如果同一名字即有变量的声明又有函数的声明,则函数的声明总是会覆盖掉变量的声明;

15. 闭包

我对闭包的定义是:
闭包的标准定义:携带外部变量的函数称为闭包;

我之所以这样对闭包下定义,是因为这个定义几乎适用所有语言的闭包,如:Object-C、Swift、JavaScript等等;所以我认为这是较标准的定义;

对于JavaScript中的闭包虽然符合标准定义,但是由于JavaScript语言的一些特性,使得JavaScript中的闭包的实现与其它语言(如:Object-C、Swift)的实现并不一样;

很多人都认为JavaScript中的闭包只会携带它内部引用的外部变量,并不会携带没有引用的外部变量,其实这是错误的;可以通过下面的代码证明:

function outFun() {
    var outArg1 = "外部参数1";
    var outArg2 = "外部参数2";
    function outArg3() {
        console.log("外部参数3");
    }


    /*定义闭包
    * codeStr:字符串类型的参数,该参数的值将被当作代码执行
    * return : 返回将codeStr作为代码执行的结果;
    * */
    function closureFun(codeStr) {
        console.log("闭包引用的变量的值:",outArg1);
        return eval(codeStr);   //返回将codeStr作为代码执行的结果;
    }

    return closureFun;
}

var getValueOf  = outFun();     //获取闭包




var arg2Value = getValueOf("outArg2");      //尝试获取闭包内没有引用的变量outArg2的值;
console.log(arg2Value);     //输出结果为:外部参数2


var arg3Value = getValueOf("outArg3");      //尝试获取闭包内没有引用的函数outArg3;
arg3Value();     //输出结果为:外部参数3

从示例代码中的运行结果中可以看出,对于闭包引用到的外部变量outArg1 和 闭包没有引用到的变量outArg2和函数outArg3,在闭包执行时都能被正确地访问到,所以闭包会携带所有的外部变量(函数也是变量);

其实闭包携带外部变量的机制并非闭包的特有机制,它是函数的作用域链的一个效应;在JavaScript中,闭包和普通函数没有任何本质的区别,闭包只是函数在某种使用场景下的一个名字,就好比凶器只是刀在用于行凶时的名字;

JavaScript中的闭包能携带外部变量的原因是:
JavaScript的函数在被创建时(被定义时)会生成自己的作用域链;该作用域链会保存各级作用域对象的引用,所以JavaScript的函数能够访问其外部的所有变量;
说见上文的《作用域链的理解》

所以,本质上,JavaScript中的闭包携带的不是外部变量,而是外部的作用域对象;

使用闭包的建议:
由于JavaScript中的函数(包括闭包)会外部的作用域链;所以,建议:

  1. 闭包的嵌套不要太深;
    闭包嵌套越深,占用的内存空间就越大;
  2. 不要使用过多的闭包;因为闭包较占内存;

16. 数组的splice()方法的参数说明

splice()方法可以接收任意数量的参数,参数的含意如下:
第1个参数:指定操作的起始项的索引;
第2个参数:指定要删除的项数;
第3个及以后的参数:指定要插入的项目;

17. 各种添加事件处理程序的特点

事件处理程序的名字以on开头,紧接个事件的名字,如:click事件的事件处理程序名字为onclick;
为事件指定处理程序的方式有好几种:

方式1. 对象的事件属性

格式为:

对象.事件属性 = 事件处理函数;

示例:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    //处理事件
}

这种添加方式有以下特点:

  1. 事件处理程序是作为对象的方法来运行的,所以事件处理程序中this的值是相应的对象;
  2. 事件处理程序会在事件流的冒泡阶段被处理;
  3. 通过给对象的事件属性设置null来移除事件;
  4. 只能同时指定一个事件处理程序;
  5. 只能把事件处理程序添加到冒泡阶段;
  6. 使用 对象的事件属性(方式1) 和 HTML的事件标签属性(方式3)这两种方式对 同一个元素 添加的事件处理器,只会最后一次设置的有效;因为 HTML的事件标签属性的方式(方式3) 最终也是通过 对象的事件属性的方式(方式1) 来添加事件处理器的;
  7. 如果事件处理程序返回假值 return false; ,则会阻止此事件的相关默认操作,即:相当于调用事件对象event的preventDefault()方法;

方式2. 元素的addEventListener方法

这种方式是通过元素的addEventListener方法添加事件处理程序,通过元素的removeEventListener方法移除事件;
所有DOM节点中都包含addEventListener和removeEventListener这2个实例方法,这2个方法的描述如下:

addEventListener方法:

语法:

element.addEventListener(event, function, useCapture)

参数值:

event:必须。字符串,指定事件名。
注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。 

function:必须。指定要事件触发时执行的函数。 
当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, "click" 事件属于 MouseEvent(鼠标事件) 对象。

useCapture:可选。布尔值,指定事件是否在捕获或冒泡阶段执行。true - 事件句柄在捕获阶段执行
        false- false- 默认。事件句柄在冒泡阶段执行;

示例:

var btn = document.getElementById(“myBtn”);
btn.addEventListener("click", function(){
    document.getElementById("demo").innerHTML = "Hello World";
});

removeEventListener方法:

语法:

element.removeEventListener(event, function, useCapture)

参数值:

event:必须。要移除的事件名称。
注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。 

function:必须。指定要移除的函数。 

useCapture:可选。布尔值,可选。布尔值,指定移除事件句柄的阶段。
true - 在捕获阶段移除事件句柄;
false- 默认。在冒泡阶段移除事件句柄;
注意: 如果添加两次事件句柄,一次在捕获阶段,一次在冒泡阶段,你必须单独移除该事件。

示例:

// 向 <div> 元素添加事件句柄
document.getElementById("myDIV").addEventListener("mousemove", myFunction);

// 移除 <div> 元素的事件句柄
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);

这种添加方式有以下特点:

  1. 可以添加多个处理事件;
  2. 通过addEventListener添加的事件处理程序,必须通过removeEventListener才能移除;
  3. 即可以把事件处理程序添加到捕获阶段,又可以添加到冒泡阶段;
  4. 事件处理程序中this的值是被添加的元素对象;
  5. 不能通过事件处理程序返回假值 return false; 来会阻止此事件的相关默认操作;

方式3. HTML的事件标签属性

格式为:

<标签名  事件处理程序名="JavaScript代码">

通过这种方式指定的事件处理代码(相应事件处理程序的标签属性(简称:事件标签属性)的值,如:“JavaScript代码”)会被放在一个函数环境中去执行;具体实现机制如下:

通过事件的标签属性指定事件的实现要机制:

  1. 浏览器会为相应事件动态地创建一个函数作为相应事件的事件处理函数,并把该函数通过对象的事件属性的方式设置给了当前元素dom对象上对应的事件处理器属性上,该函数如下:
    dom对象.事件属性 = function (event) {
        with (document){
            with (this){
                //元素的事件标签属性的值会在这里被当作JavaScript代码来执行;
                eval(事件标签属性的值);
             }
         }
    }
    
  2. 这个动态创建的事件处理函数中有一个入参,名收 event,用来接收事件对象;
  3. 事件标签属性的值会被当作JavaScript代码在这个动态创建的事件处理程序中运行:
  4. 这个动态创建的事件处理函数内部使用 withdocumentthis 对象扩展了成当前的作用域对象,所以 documentthis 的所有属性都可以在当前作用域里直接通过属性名字作为变量来直接访问;

通过这个实现机制,可知:

通过事件的标签属性指定事件的方式有以下特点:

  1. 事件标签属性的值会被当作JavaScript代码在这个动态创建的事件处理程序中运行;
  2. 使用 对象的事件属性(方式1) 和 HTML的事件标签属性(方式3)这两种方式对 同一个元素 添加的事件处理器,只会最后一次设置的有效;因为 HTML的事件标签属性的方式(方式3) 最终也是通过 对象的事件属性的方式(方式1) 来添加事件处理器的;
  3. 这个动态创建的事件处理函数中的tihs值是事件的目标元素;事件标签属性的JavaScript代码中被调用的函数的this值是 window 对象;
  4. 在事件标签属性的值的JavaScript代码中,可以直接访问 保存事件对象的变量event、绑定当前事件的dom元素this,也可以通过 document对象 和 绑定当前事件的dom对象 的任意属性名字作为变量名来直接访问;如:<input onclick="clickHandler(event,this,cookie,value)">,其中 cookiedocument对象的属性,value 是 input元素的Dom对象的属性;
  5. 只能把事件处理程序添加到冒泡阶段;
  6. 在事件标签属性的值中可以通过返回假值 return false; 来阻止此事件的相关默认操作,即:相当于调用事件对象event的preventDefault()方法;

18. change、propertychange 和 input 事件的区别

onchange 事件与 onpropertychange 事件的区别:

onchange 事件在内容改变(两次内容有可能还是相等的)且失去焦点时触发;
onpropertychange 事件却是实时触发,即每增加或删除一个字符就会触发,通过 js 改变也会触发该事件,但是该事件 IE 专有;

oninput 事件与 onpropertychange 事件的区别:

oninput 事件是 IE 之外的大多数浏览器支持的事件,在 value 改变时触发,实时的,即每增加或删除一个字符就会触发,然而通过 js 改变 value 时,却不会触发。
onpropertychange 事件是任何属性改变都会触发的,而 oninput 却只在 value 改变时触发,oninput 要通过 addEventListener() 来注册,onpropertychange 注册方式跟一般事件一样。(此处都是指在js中动态绑定事件,以实现内容与行为分离)

oninput 与 onpropertychange 失效的情况:

oninput 事件:

  • 当脚本中改变 value 时,不会触发;
  • 从浏览器的自动下拉提示中选取时,不会触发;

onpropertychange 事件:

  • 当 input 设置为 disable=tru e后,onpropertychange 不会触发;

19. void运算符

void是一元运算符,它用在操作数的前面,操作数可以是任意类型;操作数会被正常计算,void会忽略操作数的计算结果,并返回undefined;

20. textContent 与 innerText、innerHTML 的区别

Node.textContent 属性表示一个节点及其后代的文本内容。

语法

let text = element.textContent;
element.textContent = "this is some sample text";

描述

  • 如果 element 是 Document,DocumentType 或者 Notation 类型节点,则 textContent 返回 null。如果你要获取整个文档的文本以及CDATA数据,可以使用 document.documentElement.textContent。
  • 如果节点是个CDATA片段,注释,ProcessingInstruction节点或一个文本节点,textContent 返回节点内部的文本内容(即 nodeValue)。
  • 对于其他节点类型,textContent 将所有子节点的 textContent 合并后返回,除了注释、ProcessingInstruction节点。如果该节点没有子节点的话,返回一个空字符串。
  • 在节点上设置 textContent 属性的话,会删除它的所有子节点,并替换为一个具有给定值的文本节点。

与innerText的区别

Internet Explorer 引入了 node.innerText。意图类似,但有以下区别:

  • textContent 会获取所有元素的内容,包括 <script><style> 元素,然而 innerText 不会。
  • innerText意识到样式,并且不会返回隐藏元素的文本,而textContent会。
  • 由于 innerText 受 CSS 样式的影响,它会触发重排(reflow),但textContent 不会。
  • 与 textContent 不同的是, 在 Internet Explorer (对于小于等于 IE11 的版本) 中对 innerText 进行修改, 不仅会移除当前元素的子节点,而且还会永久性地破坏所有后代文本节点(所以不可能再次将节点再次插入到任何其他元素或同一元素中)。

与innerHTML的区别

正如其名称,innerHTML 返回 HTML 文本。通常,为了在元素中检索或写入文本,人们使用innerHTML。但是,textContent通常具有更好的性能,因为文本不会被解析为HTML。此外,使用textContent可以防止 XSS 攻击。

归属区别

  • textContent 是 Node 对象的属性;
  • innerHTML 是 Element 对象的属性;
  • innerText 是 HTMLElement 对象的属性;

21. 类型检查与验证

typeof

语法:
typeof运算符后跟操作数:

typeof operand

或者

typeof (operand)

参数:

  • operand 是一个表达式,表示对象或原始值,其类型将被返回;
  • 括号是可选的。

说明:
typeof 操作符返回一个字符串,表示操作数的基本类型。

下表总结了typeof可能的返回值:

类型 结果
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
宿主对象(由JS环境提供) Implementation-dependent
函数对象 "function"
任何其他对象 "object"

instanceof

语法:

object instanceof constructor

参数:

  • object : 要检测的对象
  • constructor : 某个构造函数

说明:
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

机制:
instanceof 的等效函数如下:

function instanceof(object,constructor){
    
    let nextProto = object.__proto__;
    
    while (constructor !== null) {
        if (nextProto === constructor.prototype) {
            return true;
        }
        nextProto = nextProto.__proto__;
    }

    return false;
}

或者

function instanceof(object,constructor){
    
    let nextProto = Object.getPrototypeOf(object);
    
    while (constructor !== null) {
        if (nextProto === constructor.prototype) {
            return true;
        }
        nextProto = Object.getPrototypeOf(nextProto);
    }

    return false;
}

typeof 与 instanceof 区别

  • typeof 是用来检查类型的,而 instanceof 是用来验证类型的;
  • typeof 返回的是字符串,而 instanceof 返回的是布尔值;
  • typeof 检查的是基本类型,而 instanceof 验证的是对象类型;

获取对象类型的具体类型信息

typeof 操作符只能返回操作数的基本类型,对于任何自定义的对象类型, typeof 操作符只会返回 "object" ,而 instanceof 是用来验证对象类型的,不是用来获取对象类型的;若想获取对象的具体类型信息,可通过访问对象的 constructor 属性,该属性会返回该对象的构造函数;若只想获取对象的具体类型的名字,则可通过访问对象的构造函数 constructor 的 name 属性;如下:

获取对象的类型:

object.constructor

获取对象的类型的名字:

object.constructor.name

22. Attribute和Property的区别

1. Attribute和Property的概念

  • attribute : 特性,XML 元素中的概念,用于描述 XML 标签的附加信息,即: XML 标签的特性; 如:<input type="text" value="初始值" /> 中的 typevalue 均是input元素的特性;
  • property : 属性,JavaScript 对象的概念,用于描述 JavaScript 对象的成员,即:JavaScript 对象的属性;如: JavaScript 对象 var person = {name:"郭斌勇",age:28} 中的 nameage 均是对象 person 的属性;

2. Attribute和Property的关系

为了能够在 JavaScript 中操作 HTML 浏览器会为 HTML 中的元素创建相应的 Dom 对象,Dom 对象就是普通的 JavaScript 对象,它是 HTMLElement 类型的对象,所以 dom 对象的所有成员都称为 property (属性);

JavaScript 中的 HTMLElement 类型的对象有个 attributes 属性(property),它是 NamedNodeMap 类型的类数组对象 ,它里面保存的都是 HTML 中元素的 特性(attribute) 相对应的 JavaScript 对象;在 JavaScript 中,HTML 中元素的 attribute (特性) 用 Attr 类型的对象表示;

在 XML 中,元素的特性(attribute)值是没有类型之分的,所以,无论是数字、字符中、布尔值,在 XML 眼中,它们都是同一类东西; JavaScript 中的 Attr 类型把 HTML 中的元素的特性值都映射为字符串类型;为了更好地使用 Dom , 浏览器也在 Dom 对象中为标准的 HTML 元素的特性(attribute)添加了相应的属性 property ,如:dom 对象的 idtypecheckedvaluedefaultValue 等等;为了更好的使用,浏览器为这些属性的值也做了相应的处理,所以,这些属性(property)的值与 HTML 元素中相应的特性值并非完全一致;如:对于 Dom 对象,dom.checked 的值始终是布尔类型的,dom.value 的值始终是相应 input 元素的当前值,并非是 HTML 中 input 元素的 value 特性的值,dom.defaultValue 则是 HTML 中 input 元素的 value 特性的值;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,576评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,515评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,017评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,626评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,625评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,255评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,825评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,729评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,271评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,363评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,498评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,183评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,867评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,338评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,458评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,906评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,507评论 2 359

推荐阅读更多精彩内容