JavaScript函数03

目录

  • 数据类型之间的差异性
  • 构造函数
  • 数组
    。 介绍
    。 创建
    。 数组的常规使用
    。 数组的length属性
    。 数组当中常见的操作方法
    。 数组的遍历
    。 数组的分类

数据类型之间的差异性

我们之前说过,js的数据类型分为基础数据类型引用数据类型。上面我们说过的stringbooleannumber等类型都属于基础数据类型
而我们说过的函数则属于引用数据类型
我们下面说的数组类型同样属于引用数据类型

在学习数组之前我们先来说下基础数据类型引用数据类型的区别。
基础数据类型又叫做原始数据类型。通常情况下数据会存储在内存的中。
例如,将hello,world这个字符串存储在变量str中。这个变量连同hello,world都存在中。

如果我们存在一个函数或者数组引用数据类型将其存在一个变量中,那么这个变量将会被存储在内存的中。
引用类型的数据将会被存在内存的中。
此时需要注意的是,变量和具体的 引用类型的数据是分开存储的,而存储在中的变量里面存储的只是 引用类型的数据,在 中存储的具体位置。

我们知道了基础数据类型引用类型的数据在内存中不同的存储位置之后,我们还需要知道下面的这些内容。

首先,基础类型的数据一旦创建之后不能够进行更改。
例如:

var a = "hello,world";

此时这个变量a存储的数据为一个字符串,是一个基础数据类型,我们对这个变量里面的内容,只具有读取的权限,但是并没有办法进行更改。

例如我们可以通过下标的形式进行查询:

console.log(a[0]);// h

但是我们并没有办法进行更改:

a[0] = "z";

上面的这种做法是完全行不通的。

而如果变量当中存储的数据是一个引用类型的数据,例如下面我们要学习的数组:

var a  = [10,20,30];

我们是可以进行更改的:

a[0] = "hello";

此时我们再来查看数组,是一个已经被更改的数组。

在很多的面试题当中,经常出现下面的题目:

var a = "hello,world";
var b = a;

b[0] = 'x';
console.log(b[0]); // h
console.log(a); // hello,world
console.log(b); // hello,world
当存储的数据变为引用类型的数据:

var a = [10,20,30];
var b = a;

b[0] = 'hello,world';
console.log(b); // [ 'hello,world', 20, 30 ]
console.log(a); // [ 'hello,world', 20, 30 ]

在上面的代码中,通过b对数据进行了更改,那么a也跟着发生了更改。

再来看下面的试题:

var a = "hello,world";
var b = a;
console.log(b); // hello,world
console.log(a); // hello,world

b = "admin";
console.log(a); // hello,world  变量a并没有发生变化
console.log(b); // admin 变量b相当于重新存储了数据,并且在内存中重新开辟了一块空间

在上面的案例中,我们将b重新的存储了新的数据,并不会对原本的变量a产生任何的影响。

JavaScript构造函数

JavaScript创建对象的方式有两种,一种是通过直接量的形式来创建对象,另一种则是通过构造函数的形式来创建对象。
简单的说,构造函数就是在创建对象时通过new关键字调用的函数,我们称为构造函数
通常情况下,构造函数的首字母应该大写。
我们说在js当中,一切皆对象。而所有的对象都存在一个constructor属性,我们可以查看到每一个对象的构造函数
例如:

var str = `hello,world`; // 创建一个字符串
// 查看str的构造函数
console.log(str.constructor); // [Function: String]

js当中,我们也可以通过`构造函数``来创建对象

思考:为什么要用构造函数来创建一个对象?
假设:在警局档案中有6个嫌疑人,现在我们需要通过代码来储存这几个人的信息,我们直接采用直接量对象的形式储存,写法大体如下:

var p1 =   {name: "张三", age: 30, home: "英国"};
var p1 =   {name: "里斯", age: 34, home: "伦敦"};
var p1 =   {name: "王二", age: 24, home: "瑞士"};
var p1 =   {name: "麦子", age: 34, home: "法国"};
var p1 =   {name: "麻子", age: 12, home: "华盛顿"};

在上面的代码中,我们每一次都需要创建一个完整的对象,来存储不同的信息,很大程度上造成了代码的冗余(重复)。

而我们使用构造函数来创建代码,则可以把相同的内容进行封装,从而实现代码的简化和复用。
例如:

function Criminal(name,age,home) {
this.name = name;
this.age = age;
this.home = home;
}
// 储存数据
var p1 = new Criminal( "张三",30,"英国")
var p2 = new Criminal( "里斯",34,"伦敦")
var p3 = new Criminal( "王二",24,"瑞士")
...

在上面的代码中,我们创建了一个构造函数并且通过new关键字调用构造函数创建了对象。

需要注意的是,我们之所以使用构造函数,是为了减少在使用对象直接量创建对象的时候那些重复编写的相似的代码 。

通常情况下,构造函数和普通函数其实看上去很相似,只有在通过new关键字调用的时候,才能确定一个函数到底是不是构造函数。

构造函数的执行流程。

首先,我们先来创建一个用于测试的构造函数。

function SayHello(name) {
    this.name = name ;
}
var s1 = new SayHello("zhangsan");

当我们通过new关键字来创建一个对象的时候,相当于在内存中创建了一块新的内存空间,标记为SayHello的实例

在我们的代码中,this关键字其实指向的就是存储在内存中的这个实例。

image

So,我们在使用new关键字创建对象的实例,实际上每一次调用new关键字都相当于在内存中开辟了一块新的内存空间。
this也会随着实例化对象的不同而改变指向。

所以,我们在代码中,给this添加属性其实也就相当于给未来的实例添加属性。

构造函数的返回值
在普通函数的最后可以通过return来设置函数的返回值,如果不设置返回值,那么默认返回值为undefined
但在构造函数中,如果我们不设置返回值,就将默认返回this。也就是指向内存中实例的那块内存空间,也就是相当于返回了那段存在内存空间中的对象。
而如果我们在构造函数中手动的通过return返回一个基本类型的值,那么返回值将不会收到影响,还是this
例如:

function SayHello(name) {
    this.name = name;
    return "hello,world";
}
var s1 = new SayHello("zhangsan");
console.log(s1); // SayHello { name: "zhagnsan" }

而如果return返回的是一个引用对象类型的数据,那么最终返回的则是对象。
例如:

function SayHello(name) {
    this.name = name;
    return {
            like: "I like you"
    }
}
var s1 = new SayHello("zhangsan");
console.log(s1); // { like: 'I like you' }

上面只是简单介绍了一下构造函数,构造函数的使用往往与原型原型链是分不开的。

数组

介绍
数组(array)是按次序排列的一组值。其中每一个值都有一个编号(编号默认从零开始)。

创建

  • 第一种方式:通过直接量的方式:
    例如:
var test_arr = [10,20,30] // 通过直接量的形式创建了一个数组
  • 第二种方式:通过构造函数的形式
    例如:
var test_arr = new Array();
console.log(test_arr); // []

// 也可以在使用构造函数创建的时候设置一个具体的值,例如
var test = new Array(10); // 相当于创建了一个空数组,并且长度为10
console.log(test); // [ <10 empty items> ] 打印出一个空的数组
console.log(test.lenght); // 10

var test_arr2 = new Array(10,20,30,40);
console.log(test_arr2); // [10,20,30,40]

当我们需要创建一个数组的时候,推荐使用直接量的形式来创建数组。因为无论是从创建速度还是运行效率来讲,直接量的形式都优于构造函数的形式。

下面我们来具体的说下,当我们使用构造函数创建数据的时候,与直接量创建数据的时候二者之间的区别。

当我们使用构造函数的形式来创建数组的时候,通常情况下,需要经历以下的过程:

  1. 首先创建函数
  2. 查询作用域链
  3. 创建作用域链
  4. 将数据写入

而我们使用直接量的形式来创建数据,相当于直接将数据写入内存中,速度是最快的,相当于直接作用在作用域链条中。
作用域链条es5中存在两条。一条是var,也就是变量的作用域,另外一条是函数声明。在代码执行的过程中优先查找非函数链条,顺序是从下到上,而函数链条顺序是从上到下。

通常,我们在执行js代码的阶段,通过直接量直接向内存中存储数据的速度是最快的,其次是通过变量声明,最后是通过函数调用获得的变量。

下面是关于直接量的补充扩展内容:

1976.6 ES1中,直接量有四种:null、boolean、numeric、string
1998.8 ES2中,与ES1相同
1999.12 ES3中,直接量加入了Regular
2011.6 ES5.1 与上面相同

而在ES3ES5当中,将数组对象纳于表达式一章里,称之为初始器(Initialiser)

数组的常规使用
当我们创建了一个数组,并且使用typeof查看其类型会发现其类型为object

var arr = [];
console.log(typeof arr); // object

我们可以通过instanceof来判断数组类型
例如:

var arr = [];
console.log(arr instanceof Array); // true

js当中,数组被定性为一种特殊的对象。

demo:

// 数组的常规操作
// 创建一个空数组
var arr = [];
// 通过索引值的形式向数组中添加内容
arr[0] = '张三丰';
arr[1] = '保龄球';
arr[2] = '高尔夫球';
// 打印查看数组
console.log(arr); // [ '张三丰', '保龄球', '高尔夫球' ]

// 也可以在创建数组的初期向数组中添加内容
var arr1 = [ '张三丰', '保龄球', '高尔夫球' ];
console.log(arr1); // [ '张三丰', '张无忌', '张翠山' ]

// 通过构造函数的形式来创建一个数组
var arr2 = new Array(); // 创建一个空数组
// 向数组中添加内容
arr2[0] = '张三';
arr2[1] = '李四';
arr2[2] = '王五';
console.log(arr2); // [ '张三', '李四', '王五' ]

// 通过构造函数的形式创建一个数组
var arr3 = new Array('张三','李四','王五');
console.log(arr3); // [ '张三', '李四', '王五' ]   

下面是数组的查找:

var arr = ['战死','里斯','王二'];
// 查找数组当中指定位置的元素
console.log(arr); // 打印全部的数组元素
console.log(arr[0]); // 张三 通过索引下标来查找元素
console.log(arr[2]); // 王二

当我们进行数组元素查找的时候,一旦索引值超过最大范围,则为undefined

//超过数组索引最大值 
console.log(100) // undefined

当我们要修改数组中的具体元素的时候,可以直接通过索引值找到这个元素并重新赋值。

var arr = [23,12,22];
// 通过索引值下标对数组元素中的具体元素进行修改
arr[0] = 10;
arr[2] = 30;
console.log(arr); // [10,12,30]

下面的操作主要是数组的删除操作,在下面的案例中主要通过delete运算符进行删除。

// 删除数组中的元素
var arr = ['张三';'李四';'王二'];
delete arr[0];
console.log(arr); //  [ <1 empty item>, '李四', '王五' ]   
//当我们通过delete 删除了元素之后,元素虽然被删除,但是位置依然存在,会被undefined占据
// 访问一下被删除的空位
console.log(arr1[0]); // undefined

数组的length属性
Array对象当中,存在一个lenght属性,能够用来查看数组的长度。
例如:

var arr = [10,20,30,40];
console.log(arr.length); // 4

通常情况下,我们可以通过将length属性值设置为0,来让数组清空。

var arr = [10,20,30,40];
arr.length = 0;
console.log(arr); // [] 数组被清空 

有一点需要注意,因为数组也是一种特殊的对象,所以我们通常情况下,数组索引值的设定,除了采用默认的索引值外,还可以采用自定义索引值的索引。
但是一旦我们采用了自定义索引值的索引,那么这个数组里面的length属性将会失效。

var arr = [];
arr['hello'] = 'hello world';
console.log(arr); // [hello: 'hello,world']

arr['world'] = 'this is my new world';
console.log(arr); // [ hello: 'hello,world', world: 'this is my new world' ]

console.log(arr.length); // 0

数组中常见的操作方法

  • push
  • pop
  • unshift
  • shift

push()方法:通过这个方法,我们能够将一个或者多个元素添加到数组的末尾,并且返回数组的新长度。

// 创建一个数组
var arr1 = ['张三','李四'];
// 通过push方法向数组中添加元素
arr1.push('王五');
// 检查数组内容
console.log(arr1); // [ '张三', '李四', '王五' ]

// 尝试添加多个内容
arr1.push('麻子','球子')
// 打印数组元素
console.log(arr1); ['张三','李四','王五','麻子','球子']

// 创建一个新的数组
var arr2 = [1,2,3,4];
// 尝试将arr2添加到arr1
arr1.push(arr2);
// 打印arr1;
console.log(arr1); // [ '张三', '李四', '王五', '赵六', '刘七', [ 1, 2, 3, 4 ] ]
// 向arr1中再次添加数据并且查看push方法的返回值
console.log(arr1.push('hello,world')); // 7

pop()方法能够从数组中删除最后一个元素,并且返回该元素的值,此方法更改数组的长度。

var plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];

console.log(plants.pop());
// expected output: "tomato"

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage", "kale"]

plants.pop();

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage"]

上面我们演示了pop方法删除数组的最后一个元素,但是在使用pop方法的时候,我们需要注意,该方法通过length属性来确定最后一个元素的位置,
如果不包含length属性或者length属性不能够被转换成一个数值,那么就会将length属性设置为0,并且返回undefined。
同时,你在一个空数组上调用pop方法,也会返回undefined。

arr.pop();
console.log(arr);// 并没有删除成功
console.log(arr.pop()); // undefined

unshift()方法能够将一个或多个元素添加到数组的开头,并且返回数组的新长度。

var arr = [1,2,3];
console.log(arr.unshift(4,5));
// expected output: 5

console.log(arr);
// expected output: Array [4, 5, 1, 2, 3]

shift()方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

var array1 = [1, 2, 3];

var firstElement = array1.shift();

console.log(array1);
// expected output: Array [2, 3]

console.log(firstElement);
// expected output: 1

shift 方法移除索引为 0 的元素(即第一个元素),并返回被移除的元素,其他元素的索引值随之减 1。如果length 属性的值为 0 (长度为 0),则返回 undefined
shift 方法并不局限于数组:这个方法能够通过callapply方法作用于类似数组的对象上。但是对于没有 length属性(从0开始的一系列连续的数字属性的最后一个)的对象,调用该方法可能没有任何意义。

数组的遍历

遍历指的是将对象当中的内容全部输出出来,而数组的遍历可以通过for循环和专门针对对象的for..in循环来实现。

var arr = ['hello','world','how are you','nice to meet you'];
// 循环遍历数组
for (var i = 0;i < arr.length;i++) {
    console.log(arr[i]);
}  

// 第二种遍历数组的方式
for (var j in arr) {
    console.log(arr[j]); // 同样可以实现遍历数组的目的
}
数组的分类

js当中,数组可以分为稀疏数组多维数组自定义索引数组等等。

数组是一种特殊类型的对象,所以数组的里面可以存储任意类型的数据

稀疏数组
当数组的索引值处于不连续的时候,被称为稀疏数组

var arr = [];
arr[0] = "hello";
arr[3] = "world";
arr[10] = "test";

一旦我们主动的或者被动的创建了一个稀疏数组,那么我们在遍历数组的过程中将会非常容易出现问题。

var arr = [];

arr[0] = "hello";

arr[3] = "world";

arr[6] = "test";
console.log(arr); // 我们创建了一个稀疏数组

// 循环输出数组
for(var i=0;i<arr.length;i++){
    console.log(arr[i]); // 会将空位输出undefined
}

在上面的代码中,我们创建的稀疏数组,一旦索引值不连续,那么就会形成空位。空位在js当中会被undefined代替。

多维数组
在一个数组里面存储另外的数组,这样的数组我们称之为多维数组
例如:

var arr = [[1,2,3],[4,5,6]]
console.log(arr); // [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]

我们如果想要操作多维数组,依然可以采取索引值的形式,整体的操作形式与一维数组相同。

自定义索引数组
demo:

// 自定义索引数组
    var arr = [];
    arr['name'] = '张三';
    arr['age'] = 30;
    arr['like'] = '吃喝嫖赌';
    document.write(arr); // 网页当中并没有效果
    document.write(arr['name']); // 张三
    document.write(arr['age']); // 30
    document.write(arr['like']); // 吃喝嫖赌

    console.log(arr);// [name: "张三", age: 30, like: "吃喝嫖赌"]

需要注意的是,数组因为是一种特殊类型的对象,所以数组的索引值无论是默认的数值还是我们设定的自定义索引,其实类型都是字符串类型。
即使索引值在创建之初看上去并不像是一个字符串,但是在代码的运行过程中也会被转换为字符串类型。

数组的排序

  1. 冒泡排序
    所谓的冒泡排序,其实指的是对数组中的数据进行排序,按照从小到大的顺序来进行排列。依次对数组中相邻数字进行比较(两两比较),大的放后面
// 数组的冒泡排序
<script>
        var arr = [10,3,4,2,32,43,100,99];
        maoPao(arr);
        function maoPao(arr) {
            for (var i = 0;i < arr.length-1;i++) { //大循环,用于遍历数组每个元素
                for (var j = 0;j < arr.length-i-1;j++) { // 小循环,用于将数组中的某个元素与其它所有元素相比较, arr.length-i-1相当于arr.length-(i+1),因为一次循环比较的是一个值,所以这一步求出的剩余的没有比较的次数
                    if (arr[j] > arr[j+1]) {
                        // 进行交换
                        var temp = arr[j];
                        arr[j] = arr[j+1];
                        arr[j+1] = temp;
                    }
                }
            }
                console.log(arr);
        }
    </script>
  1. 选择排序
    将第一位依次与后面的元素相比较,得到最小值,与第一位交换。再用第二位依次与后面元素相比较,得到最小值,与第二位交换。

    从原始数据中找到最小元素,并放在数组的最前面。然后再从下面的元素中找到最小元素,放在之前最小元素的后面,直到排序完成

var arr = [5,99,2,9,1,5,67,7,10,23];     //定义一个杂乱的数组
    for(var i=0;i<arr.length-1;i++){//大循环,需要比对的次数
        var min = arr[i];//假定一个最小值
        var minIndex = i;//假定最小值的索引
        for(var j=i+1;j<arr.length;j++){//小循环,每次需要比对的次数
            if(min>arr[j]){//判断最小值是否为真的最小值
                min = arr[j];//获取真正的最小值
                minIndex = j;//获取真正最小值的索引
            }
        }
        arr[minIndex] = arr[i];//将当前元素放在最小值的位置
        arr[i] = min;//将最小值放在当前元素的位置
    }
    console.log(arr);//输入排序好的数组

小练习:
随机点名

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>随机点名</title>
    <style>
        #name {
            width: 300px;
            height: 300px;
            border: 2px solid red;
            text-align: center;
            line-height: 300px;
            font-weight: bold;
            font-size: 80px;
        }
    </style>
</head>
<body>
<div id="name">
    hi!
</div>

<button onclick="start()">开始</button>
<button onclick="end()">结束</button>
</body>
<script>
    // 声明变量
    var time, // 计时器
        div,
        name; // 选中的用户名

    div = document.getElementById('name');

    // 创建一个数组用来存储数据
    var name_list = ["张三", "李四", "王五", "赵六","张1","张2","张3",'张四'];

    function start(){
        // 生成一个随机数
        var num = Math.floor(Math.random() * name_list.length);
        // 根据随机索引值来确定选中的姓名
        name = name_list[num];
        // 更改网页里div的值
        div.innerHTML = name;
        time = setTimeout("start()",100);
    }

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

推荐阅读更多精彩内容