referer、prototype、array、json笔记整理

referer、prototype、array、json笔记整理

referer

Referrer-policy

Referrer-policy作用就是为了控制请求头中referer的内容

包含以下值:

  • no-referrer : 整个referee首部会被移除,访问来源信息不随着请求一起发送。
  • no-referrer-when-downgrade : 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP).
  • origin: 在任何情况下,仅发送文件的源作为引用地址。例如 https://example.com/page.html 会将 https://example.com/ 作为引用地址。
  • origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅发送文件的源。
  • same-origin: 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
  • strict-origin: 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
  • strict-origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
  • unsafe-url: 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。(最不安全了):
    在这里插入图片描述

如何设置referer

  1. 在HTML里设置meta
<meta name="referrer" content="origin">
  1. 在<a>、<area>、<img>、<iframe>、<script> 或者 <link> 元素上的 referrerpolicy 属性为其设置独立的请求策略。
<script src='/javascripts/test.js' 
referrerpolicy="no-referrer"></script>

imgimg

绕过图片防盗链

1、利用https网站盗链http资源网站,refer不会发送
2、设置meta

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56nvO5Hb-1673295301886)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230107214522955.png)]

3、设置referrerpolicy=“no-referrer”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o6JeMPoM-1673295301887)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230107214906463.png)]

4、利用iframe伪造请求referer
#index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <!-- <meta name="referer" content="no-referrer" > -->
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <img src="https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg" >
<!--  <iframe src="./2.html" width="20%" height="200" frameborder="1" sandbox="allow-scripts allow-same-origin allow-modals "></iframe>-->
    </body>
   <script src="./1.js" ></script>
</html>
#js
function showImg(src, wrapper ) {
    let url = new URL(src);
    let frameid = 'frameimg' + Math.random();
    window.img = `<img id="tmpImg" width=400 src="${url}" alt="图片加载失败,请稍后再试"/> `;

    // 构造一个iframe
    iframe = document.createElement('iframe')
    iframe.id = frameid
    iframe.src = "javascript:parent.img;" // 通过内联的javascript,设置iframe的src
    // 校正iframe的尺寸,完整展示图片
    iframe.onload = function () {
        var img = iframe.contentDocument.getElementById("tmpImg")
        if (img) {
            iframe.height = img.height + 'px'
            iframe.width = img.width + 'px'
        }
    }
    iframe.width = 10
    iframe.height = 10
    iframe.scrolling = "no"
    iframe.frameBorder = "0"
    wrapper.appendChild(iframe)
}

showImg('https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg', document.querySelector('#container'))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oB3i1IcU-1673295301888)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230107215545769.png)]

5、客户端在请求时修改header头部
1、利用XMLHttpRequest
    // 通过ajax下载图片
    function loadImage(uri) {
        return new Promise(resolve => {
            let xhr = new XMLHttpRequest();
            xhr.responseType = "blob";
            xhr.onload = function() {
                resolve(xhr.response);
            };
    
            xhr.open("GET", uri, true);
            // 通过setRequestHeader设置header不会生效
            // 会提示 Refused to set unsafe header "Referer"
            xhr.setRequestHeader("Referer", ""); 
            xhr.send();
        });
    }// 将下载下来的二进制大对象数据转换成base64,然后展示在页面上function handleBlob(blob) {let reader = new FileReader();
​        reader.onload = function(evt) {let img = document.createElement('img');
​            img.src = evt.target.result;
​            document.getElementById('container').appendChild(img)};
​        reader.readAsDataURL(blob);}const imgSrc = "https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg";
    
    loadImage(imgSrc).then(blob => {
        handleBlob(blob);
    });

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LgSAG2yn-1673295844182)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230107220133618.png)]

上述代码运行时会发现控制台提示错误:

Refused to set unsafe header “Referer”

可以看见setRequestHeader设置referer响应头是无效的,这是由于浏览器为了安全起见,无法手动设置部分保留字段,不幸的是Referer恰好就是保留字段之一

2、利用fetch
    // 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
    function handleBlob(blob) {
        let reader = new FileReader();
        reader.onload = function(evt) {
            let img = document.createElement('img');
            img.src = evt.target.result;
            document.getElementById('container').appendChild(img)
        };
        reader.readAsDataURL(blob);
    }
    
    const imgSrc = "https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg";

    function fetchImage(url) {
        return fetch(url, {
            headers: {
                // "Referer": "", // 这里设置无效
            },
            method: "GET",  
            referrer: "", // 将referer置空
            // referrerPolicy: 'no-referrer', 
        }).then(response => response.blob());
    }
    
    fetchImage(imgSrc).then(blob => {
        handleBlob(blob);
    });

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KH64NS5g-1673295301889)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230107221202535.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1EbxUyr-1673295301889)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230110015422336.png)]

prototype

对象的继承

面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。

大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现,本章介绍 JavaScript 的原型链继承。

ES6 引入了 class 语法,基于 class 的继承不在这个教程介绍,请参阅《ES6 标准入门》一书的相关章节。

原型对象概述

构造函数的缺点

JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。

function Person (name){
    this.name = name;
}
Person.prototype.color = 'white';
var Aferican_a = new Person('person_a');
var Aferican_b = new Person('person_b');
var American_a = new Person('person_c');
American_a.color ='black';
console.info(Aferican_a.color ===American_a.color);
console.info(Aferican_a.color ===Aferican_b.color);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oOTZ5Y50-1673296061806)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109194855590.png)]

上面代码中,person函数是一个构造函数,函数内部定义了name属性和color属性,所有实例对象都会生成这两个属性,即这两个属性会定义在实例对象上面。
通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。

function Person (name){
    this.name = name;
}
Person.prototype.color = 'white';
var Aferican_a = new Person('person_a');
var Aferican_b = new Person('person_b');
var American_a = new Person('person_c');
American_a.color ='black';
Person.prototype.speak = function () {
    console.info('Speak English');
}
console.info(Aferican_a.color ===American_a.color);
console.info(Aferican_a.color ===Aferican_b.color);
Aferican_a.speak();

在这里插入图片描述
上面代码中,ta们都具有speak方法。由于speak方法是生成在每个实例对象上面,所以两个实例就生成了两次。也就是说,每新建一个实例,就会新建一个speak方法。这既没有必要,又浪费系统资源,因为所有speak方法都是同样的行为,完全应该共享。

这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。

prototype 属性的作用

JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。

下面,先看怎么为对象指定原型。JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。

function f() {}
typeof f.prototype // "object"

上面代码中,函数f默认具有prototype属性,指向一个对象。

对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。

function Animal(name) {
  this.name = name;
}
Animal.prototype.color = 'white';

var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');

cat1.color // 'white'
cat2.color // 'white'

上面代码中,构造函数Animalprototype属性,就是实例对象cat1cat2的原型对象。原型对象上添加一个color属性,结果,实例对象都共享了该属性。

原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。

Animal.prototype.color = 'yellow';

cat1.color // "yellow"
cat2.color // "yellow"

上面代码中,原型对象的color属性的值变为yellow,两个实例对象的color属性立刻跟着变了。这是因为实例对象其实没有color属性,都是读取原型对象的color属性。也就是说,当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。这就是原型对象的特殊之处。

如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。

cat1.color = 'black';

cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';

上面代码中,实例对象cat1color属性改为black,就使得它不再去原型对象读取color属性,后者的值依然为yellow

总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

Animal.prototype.walk = function () {
  console.log(this.name + ' is walking');
};

上面代码中,Animal.prototype对象上面定义了一个walk方法,这个方法将可以在所有Animal实例对象上面调用。

原型链

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOftoString方法的原因,因为这是从Object.prototype继承的。

那么,Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是nullnull没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null

Object.getPrototypeOf(Object.prototype)
// null

上面代码表示,Object.prototype对象的原型是null,由于null没有任何属性,所以原型链到此为止。Object.getPrototypeOf方法返回参数对象的原型
在这里插入图片描述
读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

注意,一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

举例来说,hasownProperty 有没有属于自己的属性
在这里插入图片描述

constructor 属性

prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

function A() {}
A.prototype.constructor === A // true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZCrFYOK-1673296212463)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109203424591.png)]
由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。

function A() {}
var a = new A();

a.constructor === A // true
a.constructor === A.prototype.constructor // true
a.hasOwnProperty('constructor') // false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TufrqtzS-1673296263478)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109203655639.png)]

上面代码中,p是构造函数P的实例对象,但是p自身没有constructor属性,该属性其实是读取原型链上面的P.prototype.constructor属性。

constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。

instanceof 运算符

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。

var v = new Vehicle();
v instanceof Vehicle // true

上面代码中,对象v是构造函数Vehicle的实例,所以返回true

instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构造函数的原型对象(prototype),是否在左边对象的原型链上。

var d = new Date();
d instanceof Date // true
d instanceof Object // true

上面代码中,d同时是DateObject的实例,因此对这两个构造函数都返回true

由于任意对象(除了null)都是Object的实例,所以instanceof运算符可以判断一个值是否为非null的对象。

构造函数的继承

举例来说,下面是一个Shape构造函数。

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qz2TE9Rn-1673296318783)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109210153396.png)]

例如:我们需要让Rectangle构造函数继承Shape

// 第一步,子类继承父类的实例
function Rectangle() {
  Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
  this.base = Shape;
  this.base();
}

// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

Rectangle.prototype继承了 Object.create(Shape.prototype)的属性;

Rectangle.prototype.constructor 继承了Rectangle的属性。
在这里插入图片描述

多重继承

JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。

function M1() {
  this.hello = 'hello';
}

function M2() {
  this.world = 'world';
}

function S() {
  M1.call(this);
  M2.call(this);
}

// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);

// 指定构造函数
S.prototype.constructor = S;

var s = new S();
s.hello // 'hello'
s.world // 'world'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKJuuGIC-1673296375560)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109211018865.png)]

上面代码中,子类S同时继承了父类M1M2。这种模式又称为 Mixin(混入)。

array

Array 对象

构造函数

Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组。

var arr = new Array(2);
arr.length // 2
arr // [ empty x 2 ]

上面代码中,Array()构造函数的参数2,表示生成一个两个成员的数组,每个位置都是空值。

如果没有使用new关键字,运行结果也是一样的。

var arr = Array(2);
// 等同于
var arr = new Array(2);

考虑到语义性,以及与其他构造函数用法保持一致,建议总是加上new

Array()构造函数有一个很大的缺陷,不同的参数个数会导致不一致的行为。

// 无参数时,返回一个空数组
new Array() // []

// 单个正整数参数,表示返回的新数组的长度
new Array(1) // [ empty ]
new Array(2) // [ empty x 2 ]

// 非正整数的数值作为参数,会报错
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length

// 单个非数值(比如字符串、布尔值、对象等)作为参数,
// 则该参数是返回的新数组的成员
new Array('abc') // ['abc']
new Array([1]) // [Array[1]]

// 多参数时,所有参数都是返回的新数组的成员
new Array(1, 2) // [1, 2]
new Array('a', 'b', 'c') // ['a', 'b', 'c']

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e4hDvoSc-1673296846022)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109220255825.png)]

可以看到,Array()作为构造函数,行为很不一致。因此,不建议使用它生成新数组,直接使用数组字面量是更好的做法。

// bad
var arr = new Array(1, 2);

// good
var arr = [1, 2];

注意,如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回undefined,但实际上该位置没有任何值。虽然这时可以读取到length属性,但是取不到键名。

var a = new Array(3);
var b = [undefined, undefined, undefined];

a.length // 3
b.length // 3

a[0] // undefined
b[0] // undefined

0 in a // false
0 in b // true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XbxlMQk-1673296902675)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109220658917.png)]

上面代码中,aArray()生成的一个长度为3的空数组,b是一个三个成员都是undefined的数组,这两个数组是不一样的。读取键值的时候,ab都返回undefined,但是a的键名(成员的序号)都是空的,b的键名是有值的

实例方法

valueOf(),toString()

valueOf方法表示对该对象求值。不同对象的valueOf方法不尽一致,数组的valueOf方法返回数组本身。

var arr = [1, 2, 3];
arr.valueOf() // [1, 2, 3]

toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式

var arr = [1, 2, 3];
arr.toString() // "1,2,3"

var arr = [1, 2, 3, [4, 5, 6]];
arr.toString() // "1,2,3,4,5,6"
push(),pop()

push`方法用于在数组的末端添加一个或多个元素并返回添加新元素后的数组长度。注意,该方法会改变原数组

var arr = [];

arr.push(1) // 1
arr.push('a') // 2
arr.push(true, {}) // 4
arr // [1, 'a', true, {}]

上面代码使用push方法,往数组中添加了四个成员。

pop方法用于删除数组的最后一个元素并返回该元素。注意,该方法会改变原数组

var arr = ['a', 'b', 'c'];

arr.pop() // 'c'
arr // ['a', 'b']

对空数组使用pop方法,不会报错,而是返回undefined

[].pop() // undefined

pushpop结合使用,就构成了“后进先出”的栈结构(stack)。

var arr = [];
arr.push(1, 2);
arr.push(3);
arr.pop();
arr // [1, 2]

上面代码中,3是最后进入数组的,但是最早离开数组。

shift(),unshift()

shift()方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组

var a = ['a', 'b', 'c'];

a.shift() // 'a'
a // ['b', 'c']
删除空数组会返回undefined
var a = ['a', 'b', 'c'];
a.shift()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGUlE3U6-1673296902676)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109221946062.png)]
shift()方法可以遍历并清空一个数组。

var list = [1, 2, 3, 4];
var item;

while (item = list.shift()) {
  console.log(item);
}

list // []

在这里插入图片描述
上面代码通过list.shift()方法每次取出一个元素,从而遍历数组。它的前提是数组元素不能是0或任何布尔值等于false的元素,因此这样的遍历不是很可靠。

push()shift()结合使用,就构成了“先进先出”的队列结构(queue)。

unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组

var a = ['a', 'b', 'c'];

a.unshift('x'); // 4
a // ['x', 'a', 'b', 'c']

unshift()方法可以接受多个参数,这些参数都会添加到目标数组头部。

var arr = [ 'c', 'd' ];
arr.unshift('a', 'b') // 4
arr // [ 'a', 'b', 'c', 'd' ]
join()

join()方法以指定参数作为分隔符将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。

var a = [1, 2, 3, 4];

a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

如果数组成员是undefinednull或空位,会被转成空字符串。

[undefined, null].join('#')
// '#'

['a',, 'b'].join('-')
// 'a--b'
slice()

slice()方法相当于切片

它的第一个参数为起始位置(从0开始,会包括在返回的新数组之中),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。

var a = ['a', 'b', 'c'];

a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]

上面代码中,最后一个例子slice()没有参数,实际上等于返回一个原数组的拷贝。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWt3NX8q-1673297170182)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109222541022.png)]

slice()方法的参数是负数,则表示倒数计算的位置。

var a = ['a', 'b', 'c'];
a.slice(-2) // ["b", "c"]
a.slice(-2, -1) // ["b"]

上面代码中,-2表示倒数计算的第二个位置,-1表示倒数计算的第一个位置。

如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组。

var a = ['a', 'b', 'c'];
a.slice(4) // []
a.slice(2, 1) // []

slice()方法的一个重要应用,是将类似数组的对象转为真正的数组。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']
Array.prototype.slice.call(document.querySelectorAll("div"));

上面代码的参数都不是数组,但是通过call方法,在它们上面调用slice()方法,就可以把它们转为真正的数组。

sort()

sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变

['d', 'c', 'b', 'a'].sort()
// ['a', 'b', 'c', 'd']

[4, 3, 2, 1].sort()
// [1, 2, 3, 4]

[11, 101].sort()
// [101, 11]

[10111, 1101, 111].sort()
// [10111, 1101, 111]

上面代码的最后两个例子,需要特殊注意。sort()方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面。
在这里插入图片描述

map()

map()方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。

var numbers = [1, 2, 3];

numbers.map(function (n) {
  return n + 1;
});
// [2, 3, 4]

numbers
// [1, 2, 3]

上面代码中,numbers数组的所有成员依次执行参数函数,运行结果组成一个新数组返回,原数组没有变化

filter()

filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组

[1, 2, 3, 4, 5].filter(function (elem) {
  return (elem > 3);
})
// [4, 5]

上面代码将大于3的数组成员,作为一个新数组返回。

var arr = [0, 1, 'a', false];

arr.filter(Boolean)
// [1, "a"]

上面代码中,filter()方法返回数组arr里面所有布尔值为true的成员。

filter()方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。

[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
  return index % 2 === 0;
});
// [1, 3, 5]

上面代码返回偶数位置的成员组成的新数组。

总结

那些方法会改变原数组

1、push()

2、pop()

3、shift()

4、unshift()

5、sort()

那些方法不会改变原数组

1、map()

2、filter()

json

JSON 对象

JSON 格式

JSON 格式(JavaScript Object Notation 的缩写)是一种用于数据交换的文本格式,2001年由 Douglas Crockford 提出,目的是取代繁琐笨重的 XML 格式。

相比 XML 格式,JSON 格式有两个显著的优点:

1、书写简单,一目了然;

2、符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON 迅速被接受,已经成为各大网站交换数据的标准格式,并被写入标准。

每个 JSON 对象就是一个值,可能是一个数组或对象,也可能是一个原始类型的值。总之,只能是一个值,不能是两个或更多的值。

JSON 对值的类型和格式有严格的规定。

  1. 复合类型的值只能是数组或对象,不能是函数、正则表达式对象、日期对象。

  2. 原始类型的值只有四种:字符串、数值(必须以十进制表示)、布尔值和null(不能使用NaN, Infinity, -Infinityundefined)。

    NAN 不是一个数字

    Infinity 无穷大 1/0

    -Infinity 无穷小 -1/0

    undefined 未定义

  3. 字符串必须使用双引号表示,不能使用单引号。

  4. 对象的键名必须放在双引号里面

  5. 数组或对象最后一个成员的后面,不能加逗号

以下都是合法的 JSON。

["one", "two", "three"]

{ "one": 1, "two": 2, "three": 3 }

{"names": ["张三", "李四"] }

[ { "name": "张三"}, {"name": "李四"} ]

以下都是不合法的 JSON。

{ name: "张三", 'age': 32 }  // 属性名必须使用双引号

[32, 64, 128, 0xFFF] // 不能使用十六进制值

{ "name": "张三", "age": undefined } // 不能使用 undefined

{ "name": "张三",
  "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
  "getName": function () {
      return this.name;
  }
} // 属性值不能使用函数和日期对象

注意,null、空数组和空对象都是合法的 JSON 值。

JSON 对象

JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据。它有两个静态方法:JSON.stringify()JSON.parse()

JSON.stringify()

JSON.stringify()可以将一个值转为 JSON 字符串

JSON.stringify('abc') // ""abc""
JSON.stringify(1) // "1"
JSON.stringify(false) // "false"
JSON.stringify([]) // "[]"
JSON.stringify({}) // "{}"

JSON.stringify([1, "false", false])
// '[1,"false",false]'

JSON.stringify({ name: "张三" })
// '{"name":"张三"}'

上面代码将各种类型的值,转成 JSON 字符串。

注意,对于原始类型的字符串,转换结果会带双引号。

JSON.stringify('foo') === "foo" // false
JSON.stringify('foo') === "\"foo\"" // true

上面代码中,字符串foo,被转成了"\"foo\""。这是因为将来还原的时候,内层双引号可以让 JavaScript 引擎知道,这是一个字符串,而不是其他类型的值。

JSON.stringify(false) // "false"
JSON.stringify('false') // "\"false\""

上面代码中,如果不是内层的双引号,将来还原的时候,引擎就无法知道原始值是布尔值还是字符串。

JSON.stringify()`方法会忽略对象的不可遍历的属性

var obj = {};
Object.defineProperties(obj, {
  'foo': {
    value: 1,
    enumerable: true
  },
  'bar': {
    value: 2,
    enumerable: false
  }
});

JSON.stringify(obj); // "{"foo":1}"

上面代码中,barobj对象的不可遍历属性,JSON.stringify方法会忽略这个属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYTEsydp-1673296424516)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109232351262.png)]

第二个参数

1、JSON.stringify()方法还可以接受一个数组,作为第二个参数,指定参数对象的哪些属性需要转成字符串。

var obj = {
  'prop1': 'value1',
  'prop2': 'value2',
  'prop3': 'value3'
};

var selectedProperties = ['prop1', 'prop2'];

JSON.stringify(obj, selectedProperties)// "{"prop1":"value1","prop2":"value2"}"

上面代码中,JSON.stringify()方法的第二个参数指定,只转prop1prop2两个属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3DS4tK9T-1673296424516)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109233738726.png)]

2、第二个参数还可以是一个函数,用来更改JSON.stringify()的返回值。

function f(key, value) {
  if (typeof value === "number") { //typeof 判断类型
    value = 2 * value;
  }
  return value;
}

JSON.stringify({ a: 1, b: 2 }, f)
// '{"a": 2,"b": 4}'

上面代码中的f函数,接受两个参数,分别是被转换的对象的键名和键值。如果键值是数值,就将它乘以2,否则就原样返回。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfoVPt4q-1673296424517)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230109234559842.png)]

参数对象的 toJSON() 方法

如果参数对象有自定义的toJSON()方法,那么JSON.stringify()会使用这个方法的返回值作为参数,而忽略原对象的其他属性。

下面是一个普通的对象。

var user = {
  firstName: '三',
  lastName: '张',

  get fullName(){
    return this.lastName + this.firstName;
  }
};

JSON.stringify(user)
// "{"firstName":"三","lastName":"张","fullName":"张三"}"

现在,为这个对象加上toJSON()方法。

var user = {
  firstName: '三',
  lastName: '张',

   toJSON: function () {
    return {
      name: this.lastName + this.firstName
    };
  };
;

JSON.stringify(user);
// "{"name":"张三"}"

上面代码中,JSON.stringify()发现参数对象有toJSON()方法,就直接使用这个方法的返回值作为参数,而忽略原对象的其他参数。

Date对象就有一个自己的toJSON()`方法。

var date = new Date('2015-01-01');
date.toJSON() // "2015-01-01T00:00:00.000Z"
JSON.stringify(date) // ""2015-01-01T00:00:00.000Z""

上面代码中,JSON.stringify()发现处理的是Date对象实例,就会调用这个实例对象的toJSON()方法,将该方法的返回值作为参数。

toJSON()方法的一个应用是,将正则对象自动转为字符串。因为JSON.stringify()默认不能转换正则对象,但是设置了toJSON()方法以后,就可以转换正则对象了。

JSON.parse()

JSON.parse()可以**将一个字符串转为 JSON值 **

JSON.parse('{}') // {}
JSON.parse('true') // true
JSON.parse('"foo"') // "foo"
JSON.parse('[1, 5, "false"]') // [1, 5, "false"]
JSON.parse('null') // null

var o = JSON.parse('{"name": "张三"}');
o.name // 张三

如果传入的字符串不是有效的 JSON 格式,JSON.parse()方法将报错。

JSON.parse("'String'") // illegal single quotes
// SyntaxError: Unexpected token ILLEGAL

上面代码中,双引号字符串中是一个单引号字符串,因为单引号字符串不符合 JSON 格式,所以报错。

为了处理解析错误,可以将JSON.parse()方法放在try...catch代码块中。

try {
  JSON.parse("'String'");
} catch(e) {
  console.log('parsing error');
}

JSON.parse()方法可以接受一个处理函数,作为第二个参数,用法与JSON.stringify()方法类似。

function f(key, value) {
  if (key === 'a') {
    return value + 10;
  }
  return value;
}

JSON.parse('{"a": 1, "b": 2}', f)
// {a: 11, b: 2}

上面代码中,JSON.parse()的第二个参数是一个函数,如果键名是a,该函数会将键值加上10。

总结

1、JSON.parse()可以**将一个字符串转为 JSON值 **
var JSON_parse = '{"name":"张三","age":"18","身高":"1.8"}';
var obj = JSON.parse(JSON_parse);
console.log(obj); //{name: '张三', age: '18', 身高: '1.8'}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eOQsbKIC-1673296587084)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230110002958857.png)]

2、JSON.stringify()可以将一个值转为 JSON 字符串
var JSON_stringify = '{name:李四, age:28 , 身高:1.8}';
var obj = JSON.stringify(JSON_stringify);
console.log(obj);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xqMlC1B2-1673296587084)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20230110003712622.png)]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值