Giter Club home page Giter Club logo

blog's People

Watchers

 avatar

Forkers

migushu

blog's Issues

JavaScript中的深拷贝与浅拷贝

值类型与引用类型

谈浅拷贝与深拷贝之前,我们需要先理清一个概念,即值类型引用类型

什么是值类型与引用类型?这要先从JS中的基本类型说起。

首先我们知道,JS中有六种基本类型,number, string, boolean, null, undefined,object。这几个类型就统共被分为两类值类型引用类型

number,string,boolean,undefined就是值类型;object就是引用类型。object里面涵盖例如数组、函数、Date对象以及Math对象等等

这里面null比较特殊,ECMA标准中将它定义为值类型,当你使用在你的编译器里执行typeof(null)时,它的返回值是object。我个人偏好于将它理解为一个指向空对象的指针,便于理解。

值类型与引用类型的存储

计算机存储值类型和引用类型的方法是不同的。这里我们需要提到两种分配内存的数据结构,

栈和堆都是一种内存的分配方式

里面的数据占据空间的大小是固定的(例如JS里的数字就固定为64bit的浮点数),空间也是相对较小的,JS里面会把值类型放到栈里面去存储,而存储的就是这个值本身。

而堆里面的数据占据空间的大小是不固定的,空间相对较大,JS会把引用类型的值放到堆里面去存储,而把这个引用类型的地址存放到栈里面去(这个保存地址的变量就是指针)。

于是,回归正题,当我们想要拷贝一个变量的值得时候,它的存储类型就决定了我们拷贝一个值的方式。

下面是《JavaScript高级程序设计》中的示意图,很清晰地表明了两者的区别。

值类型的拷贝

引用类型的拷贝

值类型的拷贝

上面我们说了,值类型是存储在栈里面的,直接存储的就是这个变量的值。那么要拷贝值类型,很直接的将这个变量赋值给另一个新的变量就行了吗,这里就不需要多说了。

引用类型的浅拷贝与深拷贝

抛开直接指向同一个地址的拷贝不谈,引用类型的浅拷贝,大多数情况下指的是只拷贝一层对象的属性。而如果对象中有不止一层属性,即对象中还包含有引用类型的话,就需要使用深拷贝,即去递归复制对象中的所有属性。

浅拷贝

浅拷贝实现起来非常简单,这里就不赘述了,在各种工具库都有对应的方法,这里列举了几种常见的。

//jQuery
let newObj = jQuery.extend({}, obj)

//es6
let newObj = Object.assign({}, obj)

//es7
let newObj = {...obj}

深拷贝

递归拷贝

这是我在做百度前端学院的2015春季题的时候实现的深拷贝代码,只考虑了对象中出现数组、对象、日期的情况。

首先需要判断拷贝的对象的类型,值类型时直接复制,引用类型时再分别考虑。因此首先写了一个判断当前变量类型的函数。

function getVarType(data) {
  //确定当前变量的类型
	if (data === undefined) {
		return 'Undefined';
	}
	if (data === null) {
		return 'Null';
	}
	return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
};

判断类型后进行拷贝,在遇到引用类型的情况时,递归调用cloneObject函数。

function cloneObject(data) {
	var objectType = getVarType(data);
	//the object for cloning is native object
	if (objectType == "null" || objectType == "undefined") {
		return data;
	}

	if (objectType == "string" || objectType == "number" || objectType == "boolean") {
		var copy = data;
		return copy;
	} else if (objectType == "date") {
		var copy = new Date();
		copy.setTime(data.getTime());
		return copy;
	} else if (objectType == "array") {
		var copy = [];
		for (var i = 0; i < data.length; i++) {
			copy[i] = cloneObject(data[i]);
		}
		return copy;
	} else if (objectType == "object") {
		var copy = {};
		for (var attr in data) {
			if (data.hasOwnProperty(attr)) {
				copy[attr] = cloneObject(data[attr]);
			}
		}
		return copy;
	}
}

让我们用在这个函数再进行一次上面的检测。

var obj = {
	"a": 1,
	"object": {
		"b": [2, 3, 4],
		"c": 3
	}
}
var newObj = cloneObject(obj);

console.log(newObj);

newObj["object"].c = 4;

console.log(obj["object"].c);//与新对象不同,这里输出的值为3

jQuery实现深拷贝

jQuery要实现深拷贝,要用到extend这个方法,在上面浅拷贝也用到了。在需要深拷贝时,只需要将第一个参数设置为true即可。

var newObj = $.extend(true,{},obj);

Merge the contents of two or more objects together into the first object.

[jQuery.extend( deep ], target, object1 [, objectN ] )

  • deep

    Type: Boolean

    If true, the merge becomes recursive (aka. deep copy). Passing false for this argument is not supported.

  • target

    Type: Object

    The object to extend. It will receive the new properties.

  • object1

    Type: Object

    An object containing additional properties to merge in.

  • objectN

    Type: Object

    Additional objects containing properties to merge in.

JSON实现深拷贝

JSON实现深拷贝非常简单粗暴:

var newObj = JSON.parse( JSON.stringify(obj) );

遗留问题

上面实现的递归深拷贝中,还存在一些问题,例如:无法实现对函数的拷贝,在遇到循环引用时会导致栈溢出。而使用JSON时,也同样存在无法处理函数以及循环引用等问题,且使用JSON实现时,原型链中的属性也无法被拷贝。

这些问题暂且留到之后解决。

JSON Schema详解

什么是JSON Schema

大部分插件都支持到draft-07及其子集,因此本文基于draft-07

JSON: JavaScript Object Notation的缩写,一种数据交换格式

JSON Schema: 一种基于JSON格式定义JSON数据结构的规范

JSON Schema官网https://json-schema.org/

JSON Schema校验: https://www.jsonschemavalidator.net/

JSON Schema生成: https://jsonschema.net/

示例

JSON数据:

{
  "module": "test.png",
  "submoduleList": [
    {
      "albumList": [
        {
          "albumId": 111111,
          "coverPath": "111111111.jpg",
          "title": "春节活动001",
          "trackId": -11
        },
        {
          "albumId": 222222,
          "coverPath": "22222222.jpg",
          "title": "春节活动002"
        }
      ]
    }
  ],
  "order": 4
}

JSON Schema:

{
    "type": "object",
    "properties": {
        "module": {
            "description": "模块标题",
            "type": "string"
        },
        "submoduleList": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "albumList": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "albumId": {
                                    "type": "number",
                                    "description": "专辑id"
                                },
                                "coverPath": {
                                    "description": "图片",
                                    "type": "string"
                                },
                                "title": {
                                    "type": "string",
                                    "description": "标题"
                                },
                                "trackId": {
                                    "type": "number",
                                    "description": "歌曲id"
                                }
                            },
                            "required": [
                                "albumId",
                                "coverPath",
                                "title"
                            ]
                        },
                        "description": "专辑列表",
                        "required": []
                    }
                },
                "required": []
            },
            "description": "子模块列表",
            "required": []
        },
        "order": {
            "type": "number",
            "description": "顺序"
        }
    },
    "required": [
        "module",
        "order"
    ],
}

JSON Schema关键字

$schema

当前schema属于哪种schema规范

{
    "$schema": "http://json-schema.org/draft-07/schema#",
}

通用关键字

metadata(title,description,default)

描述schema

{
    
    "title" : "Match anything",
    "description" : "This is a schema that matches anything.",
    "default" : "Default value"
}
关键字 说明
title 必须是string类型,对json数据进行简要说明
description 必须是string类型,完整说明json数据的意义
default 指定默认值,添加缺省的默认值

enum

指定type

 {
    "type": "string",
    "enum": ["red", "amber", "green"]
 }

不指定type时,可以不同类型混合

 {
    "enum": ["red", "amber", "green", 42, null]
 }

type

在draf-07及以后的版本中去掉了integer,因其在数据模型上与number没有差别

类型 描述
string A string of Unicode code points, from the JSON "string" value
number An arbitrary-precision, base-10 decimal number value, from the JSON "number" value
boolean A "true" or "false" value, from the JSON "true" or "false" value
null A JSON "null" value
object An unordered set of properties mapping a string to an instance, from the JSON "object" value
array An ordered list of instances, from the JSON "array" value

类型相关关键字

string

关键字 说明
minLength 字符串的最小长度(小于等于)
maxLength 字符串的最大长度(大于等于)
pattern 正则表达式
{
  "type": "string",
  "minLength": 2,
  "maxLength": 3,
  "pattern": "^(\+?86)?1(3|5|7|8)\D{9}$"
}

number

关键字 说明
multipleOf 给定数字的倍数
minimum 最小值
exclusiveMinimum boolean值,为true时,number > min,为false时,number >= min
maximum 最大值
exclusiveMaximum exclusiveMinimum
// 0 <= number < 100
{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "exclusiveMaximum": true
}

object

关键字 说明
properties 对象,每一个key对应JSON对象的每一个属性
additionalProperties booleanobject,控制properties外的属性,默认为true,即允许额外属性,false则表示不允许额外属性。当值为一个对象时,该对象为一个schema,校验不在properties中的属性
required 数组,表示properties中必须的属性
minProperties 对象中最小的属性个数
maxProperties 对象中最大的属性个数
{
    "type": "object",
    "properties": {
        "number": { "type": "number" },
        "street_name": { "type": "string" },
        "street_type": {
            "type": "string",
            "enum": ["Street", "Avenue", "Boulevard"]
        }
    },
    "additionalProperties": { "type": "number" },
    "required": ["number", "street_name"]
}

{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "street_number": 11011 }

Dependencies

schema根据特定属性的存在而改变

Property dependencies:如果给定属性存在,则某些属性必须存在

// 如果age存在,则address属性必须存在
{
    "type": "object",
    "properties": {
        "name": { "type": "string" },
        "age": { "type": "number"},
        "address": { "type": "string"}
    },
    "required": ["name"],
    "dependencies": {
        "age": ["address"]
    }
}

Schema dependencies:如果给定属性存在,则Schema发生变化

// 如果age存在,则address属性必须存在
{
    "type": "object",
    "properties": {
        "name": { "type": "string" },
        "age": { "type": "number"},
        "address": { "type": "string"}
    },
    "required": ["name"],
    "dependencies": {
        "address": {
            "properties": {
                "stree_no": { "type": "number" }
            },
            "required": ["stree_no"]
        }
    }
}

Pattern Properties

所有以a开头和b结尾的属性必须满足patternProperties中的定义,且因为additionalPropertiesfalse,所以不能有非a开头或b结尾的属性出现

{
    "type": "object",
    "patternProperties": {
        "^a": { "type": "string" },
        "b$": { "type": "integer" }
    },
    "additionalProperties": false
}

array

关键字 说明
items 数组元素,List validation每一个元素都匹配同一类型;Tuple validation定义固定长度及元素类型的数组
minItems 数组最小长度
maxItems 数组最大长度
uniqueItems 为true时保证数组中每一个元素都唯一

List validation

// [1, 2, 3,4]
{
    "type": "array",
    "items": {
        "type": "number"
    }
}

Tuple validation

// [1, 'aaaa', 'red']
{
    "type": "array",
    "items": [{
        "type": "number"
    }, {
        "type": "string"
    }, {
        "type": "string",
        "enum": ["red", "blue", "yellow"]
    }]
}

组合关键字

allOf

对给定所有的schema进行校验

{
    "allOf": [
        { "type": "string" },
        { "maxLength": 5 }
    ]
}

anyOf

匹配给定schema中的一个或多个

{
    "anyOf": [
        { "type": "string" },
        { "type": "number" }
    ]
}

oneOf

对于给定的多个Schema,只能有一个有效

{
    "oneOf": [
        { "type": "number", "multipleOf": 5 },
        { "type": "number", "multipleOf": 3 }
    ]
}

根据上面定义的Schema,10和9都能通过校验,15则不能

not

{ "not": { "type": "string" } }

if-then-else 关键字

{
  "type": "object",
  "properties": {
    "foo": {
      "type": "string"
    },
    "bar": {
      "type": "string"
    }
  },
  "if": {
    "properties": {
      "foo": {
        "enum": [
          "bar",
          "123"
        ]
      }
    },
    "required": [
      "foo"
    ]
  },
  "then": {
    "required": [
      "bar"
    ]
  },
  "else": {
    "properties": {
        "color": {
            "enum": [
              "red",
              "orange"
            ]
        }
    }
  }
}

定义

需多次引用的schema,可在根节点下definitions中定义

// #/xxx/xxx 表示从根节点开始的路径
{
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": { "type": "string" },
                "city":           { "type": "string" },
                "state":          { "type": "string" }
            },
            "required": ["street_address", "city", "state"]
        }
    }
    "type": "object",
    "properties": {
        "billing_address": { "$ref": "#/definitions/address" },
        "shipping_address": { "$ref": "#/definitions/address" }
    }
}

JSON Schema的应用

  1. 对数据进行校验
  2. 根据JSON Schema进行数据采集,通过定义的Schema生成表单,得到符合Schema定义的JSON数据

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.