JavaScript 封装对象与强迫范例转换

前面两章引见了几大数据范例以及值范例,接下来的这个知识点,我认为它关于javascript程序员来讲是很主要的,

熟悉封装对象

在最先之前,我们先看一个例子,以便以后更轻松的明白封装对象的看法。

   "tick".toUpperCase    //function toUpperCase()
   String.prototype.toUpperCase   //function toUpperCase()
   "tick".toUpperCase  === String.prototype.toUpperCase //true
   // 这里运用恒等比较推断 常量的要领是不是和Sting组织函数中的要领为同一个.

我们先来浏览以下几条知识点,以避免对下文做出更好的明白。

  1. 经由历程直接量的体式格局接见要领或属性,这类值我们称之为常量体式格局 比方上面的第一行代码。

  2. 在JavaScript中对象范例包括:对象,数组,函数这三种子范例,我们平常有两种术语,援用范例 复合值,援用范例值在做恒等比较时比较的是他们内存指向,即是不是援用同一个值.

  3. javascript对象是一种复合值,它是属性或已定名值的鸠合,经由历程.标记来读取属性值。

经由历程上面的代码我们能够看到,在运用常量体式格局接见某个要领时,依旧会返回其数据范例对应的内置组织函数要领,上面的第三点已说了,javascript经由历程.操纵符来接见属性或要领,然则常量真的是对象吗?它在接见属性的历程当中底发作了什么?别急,追随我的脚步,下面我会养精蓄锐把我晓得的,我的看法一切都说出来.

内部属性

在JavaScript中一切复合范例(如:对象,数组,函数)都包括一个内部属性[[calss]],此属机能够看做是一个内部分类。它并不是传统面向对象上的类,由因而内部属性,所以我们没法直接接见,不过,能够转换为字符串来检察.

  Object.prototype.toString.call([1,2,3]) // '[Object Array]'
  Object.prototype.toString.call(/^[1,2]$/)  // '[Object RegExp]'

这里补充一点,我们也能够经由历程此种体式格局去推断一个对象是不是为数组。

我们看到每一个差别的常量范例中的[[class]],都对应着它们响应的内部组织函数,也就是对象的内部[[Class]]属性和建立该对象的内建原生组织函数相对应,但有些惯例.

   //一说惯例,我预计就有人想到javascript中比较蛋疼的两个范例
   Object.prototype.toString.call(null) // '[Object Null]'
   Object.prototype.toString.call(undefined) // '[Object Undefined]'

除了null和undefined,其他都是javascript的内置组织函数。这里再次说一个小细节,InfinityNaN它们返回什么呢?我想不必说人人也能够猜到了,它们都属于Number范例.

  Object.prototype.toString.call(42)  // '[Object Number]'
  Object.prototype.toString.call("42") // '[Object String]' 
  Object.prototype.toString.call(true) //  '[Object Boolean]'

上面的例子除了null和undefined,它们都有各自的组织类,这些类是javascript内置的.

封装对象历程

在一样平常开辟中,我们平常不直接运用内置的组织类,而是直接经由历程常量接见.

   var arr = new String("1234")
   arr    //  {0:"1",1:"2",2:"3",3:"4"}

经由历程组织函数实例出来的常量变成了对象,实在就是手动建立其封装对象,封装对象上存在对应的数据范例要领。我们在运用常量的体式格局直接接见属性和要领时,javascript会自动为你包装一个封装对象,相当于上面我们手动包装在操纵属性或要领完成以后JavaScript也会开释当前封装对象

说到这里,我们可能会想到一个题目,假如须要常常用到这些字符串的属性和要领,比如在for轮回当中运用i<a.length,那末一最先建立一个封装对象或许更加轻易,如许JavaScript引擎就不必每次都自动建立和自动开释轮回实行这些操纵了。

实在我们的主意很好,但实际证明这并不是一个好办法,因为浏览器已为.length如许罕见状况做了机能优化,直接运用封装对象来提早优化代码反而会下降实行效力

平常状况下,我们不须要直接运用封装对象,最好是让JavaScript引擎自动挑选什么时刻应当运用封装对象,换句话说,就是应当优先斟酌运用’abc’和42如许的原始范例值,而非new String(‘abc’)和new Number(42)

看以下代码,思索它们的实行效果:

  var test = 'abc';
  test.len = 123;
  var t = test.len;

此处t为undefined,第三行是经由历程新的原始对象接见其.len属性,这并不是上次增加的.len,上次的已被烧毁,当前是一个新的封装对象.

说了这么多的理论与例子,不如我们从头至尾来整顿一下,经由历程基础范例值接见属性历程当中,究竟发作了什么。

  var s = 'hello world';
  var world = s.toUpperCase();

我们就以它为例:
起首javascript会讲字符串值经由历程new String(s)的体式格局转换为封装对象,这个对象继续了来自字符串组织函数的一切要领(这些操纵都从第二行接见要领时最先发作),当前s已变成了一个封装对象,接下来在封装对象中查找须要的要领或属性,找到了以后做出响应的操纵.一旦援用完毕,这个新建立的对象就会烧毁。这时刻s.toUpperCase已运行了该要领,随即烧毁封装对象。

拆封

想要比及封装对象中基础范例值,我们能够运用valueOf要领猎取。

    var ss = new String("123");
    ss.valueOf()  //"123"

javascipt 在须要用到封装对象中基础范例值时,会发作自动转换,即隐式强迫范例转换

  var t = new String("123");
  t+"";  "123"

不可变的原始值和可变的对象援用

javascript原始值(undefined null 字符串 数字 布尔)是不可修正的,而对象是能够被援用和修正的.讲值范例那章时我们说过JavaScript变量没有所谓的范例而言,权衡范例的是值,即值范例,在原始值上,我们没法变动,任何要领都没法变动一个原始值,这关于数字自身就说不通,怎样变动数字自身呢?然则关于字符串来讲,看似有点说得通因为它像是经由历程字符构成的数组,我们能够希冀经由历程指定的索引来修正其字符元素,但javascript并不许可这么做。字符串要领看上去返回了修正后的值,实在返回的是一个新字符串,与之前的没有任何关系.

强迫范例转换

封装对象与范例转换有很大关联,我们只要弄懂封装对象的看法,才更好的明白范例转换,另有以后的相称比较与恒等比较。

值范例转换

  var num = 123;
  //1
  num.toString(); // "123"

  //2
  num + ""; //"123"

上面两种体式格局,第一种我们称为显现强迫范例转换.第二种称之为隐式强迫范例转换。范例转换老是返回基础范例值,不会返回对象范例,

第一个,num.toString()时,把num常量经由历程内部[[class]]天生临时的封装对象,再挪用对象toString要领,返回转换完成的字符串.

第二个,因为+运算符的个中一个操纵数是字符串,所以是字符串拼接操纵,效果是数字123被转换成“123”。

引见强迫与隐式范例转换时,我们须要掌握对字符串数字和布尔范例的转换划定规矩。

  • toString

    基础范例的转换划定规矩为,undefined -> “undefined”, null->"null",true->"true",数字则运用通用划定规矩,假如有极大或极小的值则运用指数情势.
    
    var num = 1.37 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000
    num.toString() //"1.37e+21"
    
    一般对象除非自定义,不然它会返回对象内部的[[Class]]属性.
    var obj = {};
    obj.toString() //"[object Object]"
    
    数组的toStirng有些特别,它是经由历程","衔接的字符串.
    var arr = [1,2,3,4];
    arr.toString();  // "1,2,3,4"

这里捎带讲一下json字符串化.

JSON.stringify在将JSON对象序列化为字符串时也运用了toString要领,但须要注重的是JSON.stringify并不是严厉意义上的强迫范例转换,只是触及toString的相干划定规矩.

  var num = 123;
  var str = “123”; 
  JSON.stringify(num) //“123”
  JSON.stringify(str) // “”123”” //两个引号

一切平安的JSON值都能够运用JSON.stringify序列化,那末, 作甚不平安的值?比方: undefined,function,Symbol(es6新增),假如JSON中涌现这些值,序列化时不能把它们辨认,就把他们变成了null。

JSON.stringify([1,undefined,function(){}]) //"[1,null,null]"

假如对象定义了toJSON要领,会先挪用此要领,然后用它的返回值来举行序列化。

  var obj = {name:"Jack"}
  obj.toJSON = function(){ return {name:”Join"} }
  
  JSON.stringify(obj)   // “{“name”:”Join"}"

在序列化之前,会先挪用对象的toJSON要领,以它的返回值来举行序列化.默许对象是没有此属性的,假如有须要能够手动增加。

toJSON返回的不是一个经由JSON字符串化后的值,它应当是一个恰当的值,也就是没有经由任何处置惩罚的值.固然,它应当被返回一个能够被JSON化的值.

var ob = {
  val : [1,2,3],
  toJSON : function(){
    return this.val.slice(2)
  }
 }

  var obj = {
    val : [1,2,3],
   toJSON:  function(){ 
     return this.val.slice(1).join()
    }
  }
JSON.stringify(ob)  //“[2,3]"
 JSON.stringify(obj) //“”2,3”” 双引号

以上我们讲JSON字符串化完全是toString捎带出来的,它和toString很类似,然则却另有些差别。既然上面已讲了些,我们无妨再来看看它的几个参数.

很多开辟者在运用JSON字符串化时刻,只运用了第一个参数,实在它是有三个参数的。

我们先来看第一个参数和第二参数.

  var obj = { 
   a:42,
   b:”42",
   c:[1,2,3]
  }

  JSON.stringify(obj,function(k,v){ 
    if(k !== "c" ) return v
  })

第一个参数不必多引见了吧,主如果第二个参数,它有两个参数,和map的参数恰好相反,也有filter要领的那点意义,在JSON字符串化中指定哪些属性应当被处置惩罚.

等等,这里有个小细节,以上第二个回调参数实际上比我料想的多实行了一次,假定以上为例,三个属性,它第一次为undefined,第二次才是属性a。这是为何呢?因为它在处置惩罚时,obj也计入个中了。

第三个参数是缩进的字符,假如它是一个数字,就代表缩进若干空格符。假如是字符串,则牢固以它为缩进标记。

 var obj = {
   a:42,
   b:”42”,
   c:[1,2,3] 
  }

 JSON.stringify(obj,function(k,v){
   if(k !==“c”) return v
},”---")
//"{
//----"a": 42,
//----"b": "42”
//}"

在编辑代码时,因为编辑器缘由,引号的花样很不好把握,所以人人在复制代码运行时可能会失足,需搜检引号是不是为中文花样.

  • ToNumber

有时刻我们须要将非数字范例当作数字来运用,比如说数字运算

个中true转1,false转换为,null转换为0,undefined转换为NaN
toNumber时如不能转换为数字范例就会返回NaN,它对以0开首的数字并不是以16进制来处置惩罚,而是10进制

var str = “123”;
str - 0;   //123

Number(str)  //123

字符串转为number很简单,这里不做引见。让我们来看一下复合范例是怎样转换为Number范例的.
仔细读下面这句话:

为了将值转化为基础范例值,笼统操纵ToPrimite(拜见ES5范例9.1节)会起首(经由历程内部操纵DefaultValue)搜检该值是不是具有valueOf()要领,假如有就返回基础范例值,并运用该值举行强迫范例转换,假如没有就运用toString()的返回值来举行强迫转换.

`假如valueOf()和toString()均不返回基础范例值,则会发生TypeError毛病

  var obj = {
   valueOf:function(){ return "42" }
  }

  var obj_1 = {
     toString:function(){ return "42" }      
  }

  var arr = [1,2,3]
  arr.toString = function(){ return this.join(“") } //“123"

 Number(obj)  //42
 Number(obj_1) //42
 Number(arr) // 123
  • ToBoolean

关于布尔值,我们存在很多误会和疑心,须要我们特别注重.

javascript中有两个关键词true和false,离别代表布尔范例中的真和假,我们常误认认为数值1和0离别等同于true和false,在有些言语中多是如许,但在javascript中布尔值和数字时不一样的,虽然我们能够将1强迫范例转换为true,将0强迫转换为false,反之亦然,但他们并不是一回事.

我们能够把他们分为两类
(1) 能够被强迫转换为false的值
(2) 其他(被强迫范例转换为true的值)

假值
JavaScript范例详细定义了一小撮能够被强迫范例转换为false的值。
以下是一些假值:
undefined
null
false
+0 -0 和 NaN
“”
假值的布尔强迫范例转换效果为false.

虽然javascript范例没有明确指出除了假值之外都是真值,但我们能够临时明白为假值之外的值都是真值

假值对象不是假值

  var  bool = new Boolean(false);
  var number = new Number(0);
  var string = new String(“0”);

这些都是假值封装后的对象,让我们来用复合前提来推断一下.

  var a = new Boolean( bool && number && string ); 
  a   //true

我们都晓得web端的javascript依靠两个环境,javascript言语引擎,与宿主环境,宿主环境包括 DOM BOM,我们为何说起它们呢?
因为document内里有一个类数组对象它会被转换为false,它包括了页面中一切的元素。

  var dom = document.all;
  Boolean( dom )  //false

真值

 var a = “false”
 var b = “0”
 var c = “‘'"
 Boolean( a && b && c ) // true

上例的字符串看似假值,但一切字符串都是真值,不过””除外,因为它是假值列表中的唯一字符串.
真值列表能够无穷长,没法一一列举,所以我们以假值作为参考。

真值有很多,能够无穷延伸,[],function,{} 都是真值。人人能够用一点时候去掌握台上演习。

    原文作者:一只会飞的猪
    原文地址: https://segmentfault.com/a/1190000009083414
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞