JavaScript函数、对象

234 阅读13分钟

一、函数

1. 函数概念

函数也叫做功能、方法,它可以将一段代码一起封装起来,被封装的函数具备一项特殊的功能,内部封装的一段代码作为一个完整的结构体,要执行就都执行,不执行就都不执行。函数就是封装一段代码,可以重复使用。

2. 函数定义(声明)

函数命名规则:可以使用字母、数字、下划线、$,不能用数字作为开头,区分大小写,不能用关键字和保留字。

//定义函数
function 函数名fun(参数){
  封装的结构体;
}
//调用函数
fun();

3. 函数的参数

  • 形式参数:定义的()内部的参数,本质是变量,可以接受实际参数传递过来的数据,简称形参。

  • 实际参数:调用的()内部的参数,本质是传递的各种类型的数据,传递给每个形参,简称实参。

  • 函数执行的过程,伴随着传参的过程:

    IMG_0488
  • 一般自己封装的函数或者其他人封装的函数需要有一个API接口说明,告诉用户参数需要传递什么类型的数据,实现什么功能。

4. 函数表达式

定义方法:将函数的定义、匿名函数赋值给一个变量,相当于将函数矮化成了一个表达式。

匿名函数:函数没有函数名。

调用函数表达式,是给变量名加()执行,不能使用函数名加()执行。

//定义函数表达式
var foo = function fun(){
  console.log(1);
};
//匿名函数定义(多用)
var foo2 = function (){
  console.log(2);
};
//调用函数
foo();
foo2();
​

5. 函数的数据类型

例如,可以把函数作为另一个函数的参数,在另一个函数中调用。或者,可以把函数作为返回值从函数内部返回。

函数是一种单独的数据类型function,可以参与其他程序。

//定义函数表达式
var foo = function fun(){
  console.log(1);
};
//检测函数的数据类型
console.log(typeof(fun)); //-->function
console.log(typeof(foo)); //-->function//把函数作为另一个函数的参数
setInterval(function(){
  console.log(i);
},1000);
//把函数作为返回值从函数内部返回。
function fn(b){
  var a = 10;
  return function (){
    alert(a + b);
  };
}

6. arguments对象

  • JS中,arguments对象是一个比较特别的对象,实际上是当前函数的一个内置属性,也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此可以进行遍历。
  • 函数的形参和实参的数量可以不一致,所有的实参都会存储在函数内部的arguments类数组对象中。
  • 可以通过arguments.length获取用户传递了多少个实参。

7. 作用域链和遮蔽效应

不同层次的函数内部可能定义相同名字的变量,变量使用时会优先从自己所在层作用域查找变量,如果当前层没有回按照顺序从本层往外层依次查找,直到找到第一个变量定义,这个内层变量遮蔽外层变量的效果,叫做“遮蔽效应”。

8. 不写var关键字的影响

在函数内部定义新的变量,不写var,相当于定义的是全局变量,如果全局也有相同标识符,会被函数内部的变量影响。

9. 预解析和声明提升

预解析
  • 在预解析过程中,所有定义的变量都会将声明的过程提升到所在的作用域最上面,在代码执行过程中,按先后顺序回先执行被提升的声明变量过程。
  • 提升过程中,只提升声明过程,不提升变量赋值,相当于变量定义未赋值,变量内存储undefined值。
// var a; 相当于存了一个undefined值
console.log(a); //-->undefined
var a = 1;
函数声明提升

在预解析之后的代码执行过程中,函数定义过程已经在最开始就会执行,一旦函数定义成功,后续无论在哪里都可以直接调用函数。在前面调用后面定义的函数,不会报错且能顺利执行。

//先调用函数
fun(); 
function fun(){
  console.log(2); //-->可以得到2,相当于把函数提升在最前面
}
提升顺序
  • 先提升var变量声明,再提升function函数声明。
  • 假设变量名和函数名相同,那么后提升的函数名标识符会覆盖先提升的变量名,那么在后续代码中调用标识符时,内部是函数的定义过程,而不是undefined。
  • 建议:不要书写相同的标识符给变量名或函数名,避免出现覆盖。
console.log(a); //——>undefined
console.log(fun); //——>fun(){console.log(2);}
var a = 1;
var fun = "haha"; //相当于没有声明过程,只有赋值为“haha”
fun(); //此时“haha”覆盖了函数,调用的不是一个函数了,报错
function fun(){
  console.log(2);
}
  • 函数表达式进行的是变量声明提升
console.log(foo); //——>undefined,不是函数fun(){console.log(2);}
foo();
var foo = function fun(){
  console.log(2);
};

10. IIFE 自调用函数

IIFE: immediately-invoked function expression, 即时调用的函数表达式,也称为自调用函数,表示函数在定义时就立即调用。

//函数表达式定义的方式, 可以在定义时被立即执行
var foo = function fun(){
  console.log(1);
}();
  • 函数矮化成表达式,就可以实现自调用。

  • 函数矮化成表达式的方法:让函数参与一些运算,即给函数前面加一些运算符。

    • 数学运算符:+ - ()
    • 逻辑运算符:! 非运算
    //添加操作符
    + function fun(){
      console.log(1);
    }();
    - function fun(){
      console.log(1);
    }();
    ( function fun(){
      console.log(1);
    })();
    ! function fun(){
      console.log(1);
    }();
    ​
    fun(); //——>报错Uncaught ReferenceError: fun is not defined
  • IIFE关住了函数的作用域,在外面调用不了函数。

    //常用的IIFE结构
    (function(a){
      console.log(a);
    })(4);
    

二、对象

1. 对象字面量

  • 对象字面量语法:{}

  • 内部可以存放多条数据,数据与数据之间用逗号分隔

  • 每条数据都有属性名和属性值组成,键值对写法: k: v

  • k: 属性名 v:属性值,可以是任意类型的数据

  • 语法:

    var obj = { k: v, k: v, k: v};
    
  • 区分属性和方法:

    属性:对象的描述性特征,一般是名词,相当于定义在对象内部的变量。

    方法:对象的行为和功能,一般是动词,定义在对象中的函数。

2. 对象数据的调用和更改

  • 调用属性:

    对象名.属性名 person1.name

    对象名["属性名"] person1["name"]

    在对象内部:this.属性名 this.name

  • 调用方法:

    对象名.方法名() person1.sayHi()

    对象名"方法名" person1["sayHi"]();

  • 更改属性的属性值:先调用,再赋值。例如:person1.age = 19;

  • 增加新属性和属性值:直接定义,再赋值。例如:person1.weight = 140;

  • 删除一条属性:用delete关键字,空格加属性。例如:delete person1.sex;

3. 创建对象的方法

(1) new Object() 创建对象

Object()构造函数是一种特殊的函数,用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

  • 构造韩素用于创建一类对象,首字母要大写。

  • 构造函数要和new一起使用才有意义。

  • new在执行时会做的四件事情:

    1. new会在内存中创建一个新的空对象
    2. new会让this指向这个新的对象
    3. 执行构造函数 目的:给这个新的对象加属性和方法
    4. new自动生成返回值,会返回这个新对象

(2) 工厂方法创建对象

作用:便于多次创建。

function creatPerson(name, age, sex){
  //创建对象
  var person = new Object();
  //添加属性和方法
  person.name = name;
  person.age = age;
  person.sex = sex;
  person.sayHi = function(){
    console.log("hello");
  };
  //将对象作为函数的返回值
  return person;
}
//想创建对象可以调用工厂函数
var p1 = creatPerson("zs", 18, true);
var p2 = creatPerson("ls", 19, false);
console.log(p1);
console.log(p2);

(3) 自定义构造函数方法创建对象

函数内部不需要new一个构造函数的过程,直接使用this代替对象进行属性和方法的书写,也不需要return一个返回值。比工厂方法更方便。

使用时,利用new关键字调用自定义的构造函数即可。

注意:构造函数的函数名首字母需要大写,区别于其他普通函数。

function Person(name, age, sex){
  //不需要使用new一个新对象,直接用this替代将来创建的新对象
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.sayHi = function(){
    console.log("hi");
  };
  //不需要return
}
var p1 = new Person("zs", 18, true);
console.log(p1);

4. 对象遍历

for (var k in person1(对象名)){
    //k存储的是每一条数据的属性名或者方法名
    console.log(k + "属性的属性值为" +person1[k]);
}

5. 简单数据类型和复杂数据类型

(1) 堆和栈

  1. 栈:由操作系统自动分配释放,存放函数的参数值,局部变量的值等。
  2. 堆:存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。

(2) 简单数据类型在内存中的存储

单独开辟内存空间存储,互相之间不会有影响。

(3) 复杂数据类型在内存中的存储

如果将复杂数据类型的数据赋值给一个变量,复杂类型的数据会在内存中创建一个原型,而变量中存储的是指向对象的一个地址,如果将变量赋值给另一个变量,相当于将地址复制一份给了新的变量,两个变量的地址相同,指向的是同一个原型,不论通过哪个地址更改了原型,都是在原型上发生的更改,两个变量下次访问时,都会发生变化。

var p1 = {
  name : "zs",
  age : 18,
  sex : "male"
};
var p = p1;//p1将内部存储的指向对象原型的地址复制给了p
//两个变量是联动的关系,一个变化,会引起另一个变化
p.name = "ls";
console.log(p);//——>name:"ls"
console.log(p1);//——>name:"ls"

注意:对象是数组也是一样。

6. MDN

网站:developer.mozilla.org/zh-CN/

用于查询html、css、html5的应用。

  • 如何学习一个方法?

    1. 方法的功能
    2. 参数的意义和类型
    3. 返回值意义和类型
    4. demo进行测试

7. 数组

(1) 创建数组对象

var arr = new Array();

(2) 检测数组类型

instanceof 检测是否是某个对象类型

例如:arr instanceof Array

(3) 数组常用方法

  • toString()方法

    把数组转换成字符串,用逗号分隔每一项。

    var arr[1,2,3,4];
    console.log(arr.toStirng());//——>1,2,3,4
    
  • 首位数据操作

    方法功能
    push()在数组末尾添加一个或多个元素,参数随意,返回数组操作后的新长度
    pop()删除数字最后一项,不需要传参,返回删除项
    shift()删除数组第一项,不需要传参,返回删除项
    unshift()在数组开头添加一个或多个元素,参数随意,返回数组新的长度
  • 合并和拆分

    • concat():将两个数组合并成为一个新的数组,原数组不受影响,参数位置可以是一个数组字面量、数组变量、零散的值。
    • slice(start, end):从当前数组中截取一个新的数组,不影响原来的的数组,返回一个新数组,包含从start到end(不包括该元素)的元素。

      参数区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。参数可以只传递一个,表示从开始位置截取到字符串结尾。

  • 删除、插入、替换

    splice(index, howmany,element1,element2...)

    index: 删除元素的开始数组下标

    howmany:删除元素的个数,可以是0

    element:要替换的新的数据,可以是字符串

var arr = [1,2,3,4,5,6,7,8,9];
//删除功能
console.log(arr.splice(2,5));
console.log(arr);//——>[1,2,8,9]//替换功能
arr.splice(2,5,"haha","hello");
console.log(arr);//——>[1,2,"haha","hello",8,9]// 插入功能,删除的元素个数为0
arr.splice(2,0,"haha");
console.log(arr);//——>[1,2,"haha",3,4,5,6,7,8,9]
  • 查找位置

    indexOf(): 查找数据在数组中最先出现的下标,参数为要查找的数据

    lastIndexOf(): 查找数据在数组中最后一次出现的下标,参数为要查找的数据

    注意:没有查找到返回-1。

  • 倒叙

    reverse(): 将数组倒叙排列

  • 排序

    sort(): 默认按照字符编码顺序从小到大排列。如果想要根据数值大小进行排序,必须添加sort的比较函数参数,该函数需要比较a、b两个参数的值,返回一个说明这两个值的相对顺序的数字。

    返回值是-1:a排在b前面

    返回值是1:a排在b后面

    返回值是0:a和b的顺序不变\

    // 从大到小降序排列
    arr.sort(function(a,b){
      if(a > b){
        return -1;//表示a要排在b的前面 
      }else if(a < b){
        return 1; //表示a要排在b后面
      }else {
        return 0;//表示a和b不换位置
      }
    });
    
  • 转字符串方法:将数组的所有元素连接到一个字符串中。

    join():

    var arr = [1,2,3,4,5,6,10,20,30];
    var str = arr.join("");
    console.log(str);//——>123456102030
    var str = arr.join("*");//——>1*2*3*4*5*6*10*20*30
    
  • 清空数组

    方式1:arr = [];

    方式2:arr.length = 0;

    方式3:arr.splice(0, arr.length);

8. 基本包装类型

基本数据类型:byte、short、int、long、float、double、boolean、char。

Js提供的特殊的简单类型对象:String。

基本包装类型:(基本类型的数据str)在进行一些特殊操作(调用方法)时,会暂时被包装成一个临时对象(String类型),(再调用方法),结束后再销毁临时对象。

// 模拟计算机的工作
var str = "这是一个字符串";
// 进行了一个临时的包装
var str4 = new String(str);
var str2 = str4.slice(3,5);
//销毁临时对象
str4 = null;

9. 字符串

(1) 特点

字符串的所有方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。

由于字符串的不可变,在大量拼接字符串的时候会有效率问题。每拼接一次会占一个内存,再添加一个新的字符串,加载速度会变慢。

// 大量拼接字符串时,会有效率问题
var sum = "";
for (var i = 1 ; i <= 1000000 ; i++) {
  sum += i;
}
console.log(sum);

(2) 方法

方法功能
str.length长度
charAt()返回指定下标位置的字符。参数是index字符串的下标。
indexOf()返回子串在原始字符串中第一次出现位置的下标。没有的话返回-1。
concat()字符串拼接
split()分割字符串成一个数组。参数是分割符。
toUpperCase()将所有英文字符转换成大写
toLowerCase()将所有英文字符转换成小写
slice(start, end)从当前字符串中截取一个新的字符串,包含从start到end(不包括该元素)的元素。参数区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。end可以不写,表示从开始位置截取到字符串结尾。
substr(start, howmany)从开始位置截取一定长度。start区分正负,正值表示下标位置,负值表示从后面往前数第几个位置。howmany必须为正数,可以不写,表示从开始位置截取到字符串结尾。
substring(start, end)用于提取字符串中介于两个指定下标之间的字符。执行方法之前会比较两个参数大小,用小的当作开始位置,大的当作结束位置。从开始位置截取到结束位置(不包含)。