franose371 / blog Goto Github PK
View Code? Open in Web Editor NEW我的博客
我的博客
谈浅拷贝与深拷贝之前,我们需要先理清一个概念,即值类型与引用类型。
什么是值类型与引用类型?这要先从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要实现深拷贝,要用到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实现深拷贝非常简单粗暴:
var newObj = JSON.parse( JSON.stringify(obj) );
上面实现的递归深拷贝中,还存在一些问题,例如:无法实现对函数的拷贝,在遇到循环引用时会导致栈溢出。而使用JSON时,也同样存在无法处理函数以及循环引用等问题,且使用JSON实现时,原型链中的属性也无法被拷贝。
这些问题暂且留到之后解决。
主要记录在工作中遇到的一些问题及解决的方法。
大部分插件都支持到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"
],
}
当前schema属于哪种schema规范
{
"$schema": "http://json-schema.org/draft-07/schema#",
}
描述schema
{
"title" : "Match anything",
"description" : "This is a schema that matches anything.",
"default" : "Default value"
}
关键字 | 说明 |
---|---|
title | 必须是string类型,对json数据进行简要说明 |
description | 必须是string类型,完整说明json数据的意义 |
default | 指定默认值,添加缺省的默认值 |
指定type
{
"type": "string",
"enum": ["red", "amber", "green"]
}
不指定type时,可以不同类型混合
{
"enum": ["red", "amber", "green", 42, null]
}
在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 |
关键字 | 说明 |
---|---|
minLength | 字符串的最小长度(小于等于) |
maxLength | 字符串的最大长度(大于等于) |
pattern | 正则表达式 |
{
"type": "string",
"minLength": 2,
"maxLength": 3,
"pattern": "^(\+?86)?1(3|5|7|8)\D{9}$"
}
关键字 | 说明 |
---|---|
multipleOf | 给定数字的倍数 |
minimum | 最小值 |
exclusiveMinimum | boolean值,为true时,number > min ,为false时,number >= min |
maximum | 最大值 |
exclusiveMaximum | 同exclusiveMinimum |
// 0 <= number < 100
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMaximum": true
}
关键字 | 说明 |
---|---|
properties | 对象,每一个key 对应JSON 对象的每一个属性 |
additionalProperties | boolean 或object ,控制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 }
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"]
}
}
}
所有以a
开头和b
结尾的属性必须满足patternProperties
中的定义,且因为additionalProperties
为false
,所以不能有非a开头或b结尾的属性出现
{
"type": "object",
"patternProperties": {
"^a": { "type": "string" },
"b$": { "type": "integer" }
},
"additionalProperties": false
}
关键字 | 说明 |
---|---|
items | 数组元素,List validation 每一个元素都匹配同一类型;Tuple validation 定义固定长度及元素类型的数组 |
minItems | 数组最小长度 |
maxItems | 数组最大长度 |
uniqueItems | 为true时保证数组中每一个元素都唯一 |
// [1, 2, 3,4]
{
"type": "array",
"items": {
"type": "number"
}
}
// [1, 'aaaa', 'red']
{
"type": "array",
"items": [{
"type": "number"
}, {
"type": "string"
}, {
"type": "string",
"enum": ["red", "blue", "yellow"]
}]
}
对给定所有的schema
进行校验
{
"allOf": [
{ "type": "string" },
{ "maxLength": 5 }
]
}
匹配给定schema
中的一个或多个
{
"anyOf": [
{ "type": "string" },
{ "type": "number" }
]
}
对于给定的多个Schema
,只能有一个有效
{
"oneOf": [
{ "type": "number", "multipleOf": 5 },
{ "type": "number", "multipleOf": 3 }
]
}
根据上面定义的Schema
,10和9都能通过校验,15则不能
{ "not": { "type": "string" } }
{
"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
进行数据采集,通过定义的Schema
生成表单,得到符合Schema
定义的JSON
数据A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.