JS中函数的本质,定义、调用,以及函数的参数和返回值
要用面向对象的方式去编程,而不要用面向过程的方式去编程
对象是各种类型的数据的集合,可以是数字、字符串、数组、函数、对象……
对象中的内容以键值对方式进行存储
对象要赋值给一个变量
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
上面是对象的字面量的创建方式,简单直接
除此之外,还有对象的构造函数的创建方式
var cat=new Object();
还有JavaScript5新增的一种方式
该方式在老版本的浏览器中存在兼容性问题
Object.create();
获取、设置、添加、修改对象的值:
对象.属性名=属性值
对象[ 属性名 ]=属性值
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
cat.name="喵喵1";
cat.age=6;
cat.type="英短";
console.log(cat.name);//喵喵1
console.log(cat["age"]);//
console.log(cat.type);//英短
删除对象的属性:
delete 对象.属性
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
cat.type="英短";
console.log(cat.type);//英短
delete cat.type;
console.log(cat.type);//undefined
检测对象是否拥有某个属性:
属性名 in 对象
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
console.log("name" in cat);//true
console.log("type" in cat);//false
对象的枚举,遍历对象中的各个属性
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
for(var p in cat){
console.log(p);
//name age family speak friend
}
var cat={
"name":"喵1",
"age":4,
"family":["喵爸","喵妈"],
"speak":function(){
console.log("喵喵~");
},
"friend":{
"name":"喵2",
"age":5
}
}
for(var p in cat){
console.log(p+": "+cat[p]);
console.log(p);//获取属性名
//console.log(cat.p);// 写法错误
console.log(cat[p]);//获取属性值 写法正确
console.log(cat["n"+"ame"]);//喵1 []中可以添加字符串的拼接等操作
}
匿名函数,如:
window.onload=function(){
}
函数一次执行完毕之后,会将局部作用域和局部变量销毁,因此外部无法调用到
但函数本身并没有被销毁,可以进行多次调用执行
为什么要使用函数:
代码复用(自己的代码和别人的代码,如jquery)
统一修改和维护
增加程序的可读性
函数的本质:对象
定义方式:字面量定义、构造函数定义
//字面量定义
function add(n1,n2){ }
//构造函数定义
new Function("n1","n2","....");
函数和对象一样,可以添加属性和方法
function person(){
console.log("cyy");
}
//添加属性
person.age=25;
//添加方法
person.speak=function(words){
console.log(words);
}
console.log(person.age);//
person.speak("hh~");//hh~
person();//cyy
函数可以作为数据值使用:
作为数据值保存在一个变量中
var fn=function(){
return "这是一个函数";
}
console.log(fn());//这是一个函数
console.log(fn);
/*
ƒ (){
return "这是一个函数";
}
*/
此时fn打印出来的就是函数本体
函数也可以作为参数来使用:
function fn(){
alert(1);
}
setTimeout(fn,1000);//此处需要传函数本体
//此处不能加括号,如果加了括号,会立刻调用,而不是等到1秒之后
函数可以作为返回值使用:
function fn(){
return function(){
console.log("fn中的fn");
}
}
//调用
var newFn=fn();
newFn();//fn中的fn
// 或者
fn()();//fn中的fn
函数的三种定义方式
// 字面量方式
// function 声明
function add(){
}
// var 赋值表达式
var add=function(){
};
//构造函数
var add=new Function("num1","num2","return num1+num2");
add();
区别:
字面量方式比构造函数方式简洁
最重要的是预解析的区别
funtion声明的函数,可以先调用,再创建
函数预解析的时候会提前定义
add();
function add(){
return 1;
}
用var赋值表达式创建的函数,如果先调用,再创建,会报错
因为var在预解析时赋值为undefined
add();
var add=function(){
return 1;
};
function声明和var赋值表达式声明,这两种都是很好的选择
构造函数过于复杂,不推荐使用
函数定义的位置
全局作用域下的函数,在哪里都能调用
add();
function add(){
add();
}
add(); function fn(){
add();
}
局部作用域下的函数
//fn(); 无法调用
function add(){
fn();
function fn(){
fn();
function fn3(){
fn();
}
}
function fn2(){
fn();
}
}
//fn(); 无法调用
里层可以访问外层的函数,外层不能访问里层的函数
代码块中定义的函数:
由于js中没有块级作用域,所以依然是处于全局作用域中
都会出现预解析中函数被提前声明
if(true){
function fn1(){
}
}else{
function fn2(){
}
}
改成下面这样就可以实现按照条件进行声明,也是因为预解析的机制
if(true){
var fn1=function (){
}
}else{
var fn2=function fn2(){
}
}
对象中的函数
使用对象.函数名进行调用
var person={
name:"cyy",
setAge:function(age){
this.age=age;//this指向当前对象
}
}
person.setSex=function(sex){
this.sex=sex;
}
person.setAge(25);
person.setSex("girl");
console.log(person.age);//
console.log(person.sex);//girl
普通函数的调用:
命名函数的调用
function add(){
}
add();
匿名函数的调用:
如果直接在匿名函数后面加上括号进行调用,会报错
function(){
alert(1);
}();//Uncaught SyntaxError: Unexpected token (
解决方法是,将这段匿名函数执行的代码,赋值给一个变量
var fn=function(){
alert(1);
}();//
第二种解决方法:
将函数用括号括起来,实现匿名函数自执行
(function(){
alert(1);
})();//
括号把整体括起来也能实现一样的效果
(function(){
alert(1);
}());//
或者在function前加上合法的字符也可以,如!+-~
!function(){
alert(1);
}();//
或者放在console.log里面
console.log(function(){
alert(1);
}());
以上这些方式的共同目的,就是不让匿名函数的function在开头位置出现
递归调用:
自己调用自己
实现阶乘
function fn(num){
if(num<=1) return 1;
return num*fn(num-1);
}
console.log(fn(5));
/*
return 5*fn(4)
return 5*4*fn(3)
return 5*4*3*fn(2)
return 5*4*3*2*fn(1)
return 5*4*3*2*1
*/
匿名函数也是函数,当它自执行的时候,会创建自己的函数内部作用域,在执行完毕之后会被销毁,因此在外部无法访问到自执行的匿名函数内部
//此处创建函数内部作用域
(function add(n1,n2){
return n1+n2;
})();
console.log(add(3,4));//在全局无法访问到函数内部的函数add
方法的调用:
对象中的方法,使用对象.方法名进行调用
var operation={
add:function(n1,n2){
return n1+n2;
},
sub:function(n1,n2){
return n1-n2;
}
}
console.log(operation.add(3,4));//
以下这种也是方法,是点击浏览器时浏览器自动帮我们完成调用;
也可以使用方法调用的方式来进行调用
document.onclick=function(){
alert(1);
}
document.onclick();//等同于点击屏幕的效果
关于对象中的属性,什么时候加引号,什么时候不加引号
对于合法的标识符,加不加引号都可以;
不合法的标识符,必须加引号,否则会引起报错
var operation={
add:function(n1,n2){
return n1+n2;
},//合法的属性名可以不加引号
sub:function(n1,n2){
return n1-n2;
},
"@":function(){
}//不合法的属性名,会引起报错,必须加引号
}
合法的标识符,调用时使用对象.方法名即可
非法的标识符,调用时使用对象[ " 方法名 " ]
var operation={
add:function(n1,n2){
return n1+n2;
},//合法的属性名可以不加引号
sub:function(n1,n2){
return n1-n2;
},
"@":function(word){
alert(word);
}//不合法的属性名,会引起报错,必须加引号
}
console.log(operation.add(2,5));//
console.log(operation["@"]("hh~"));//hh~
[ ] 加引号和不加引号的区别
var operation={
add:function(n1,n2){
return n1+n2;
},//合法的属性名可以不加引号
sub:function(n1,n2){
return n1-n2;
},
"@":function(word){
return word;
},//不合法的属性名,会引起报错,必须加引号
key:function(n1,n2){
return "key~";
}
}
var key="add";
console.log(operation.key(2,3));//key~
console.log(operation["key"](2,3));//key~
console.log(operation[key](2,3));//
方法的链式调用
如jquery
$("p").html("html").css("color","red")....
对象中要使用链式调用,则方法中需要返回当前对象
var operation={
add:function(n1,n2){
console.log(n1+n2);
return this;
},
sub:function(n1,n2){
console.log(n1-n2);
return this;
}
}
operation.add(5,3).sub(4,2);
//要保证operation.add(5,3)能够返回operation对象
//就需要添加return this

构造函数的调用:
构造函数命名时一般首字母大写
调用时用new+函数名,返回值是一个对象
function Person(){
}
var obj=new Person();
js中内置的构造函数,常见的有:
Object()
new Object() Array()
new Array()
通过new关键字来调用
用构造函数的方式定义对象和数组,并添加内容
var person=new Object();
person.name="cyy"; var arr=new Array();
arr[0]=1;
函数的间接调用
.call 第一个参数是改变this的指向,后面传递参数的方式就是一个一个传
.apply 第一个参数是改变this的指向,后面传递参数的方式是通过数组来传递(或者类数组)
var name="cyy";
var person={};
person.name="cyy2";
person.getName=function(){
return this.name;//此处的this指向person对象
}
console.log(person.getName());//直接调用 cyy2 console.log(person.getName.call(window));//间接调用,此时this被指向了window,返回的是window.name cyy
console.log(person.getName.apply(window));//间接调用 cyy
function add(n1,n2){
return n1+n2;
}
console.log(add(1,2));//直接调用 3
console.log(add.call(window,1,2));//间接调用 3
console.log(add.apply(window,[1,2]));//间接调用 3
function add(n1,n2){
return n1+n2;
}
var arr=[4,6];
console.log(add.apply(window,arr));//
只有函数拥有call和apply方法,两者唯一的区别在于它们的传参方式
函数的参数
参数传递的本质是将实参赋值给形参
参数的个数
1、形参个数=实参个数
function add(n1,n2){
return n1+n2;
}
console.log(add(3,5));
2、实参个数 < 形参个数
多用于有可选参数的情况
function pow(base,pow=2){
return Math.pow(base, pow);
}
console.log(pow(3));//
console.log(pow(3,3));//
3、实参个数 > 形参个数
如果无法得知有多少个实参,可以使用arguments
arguments是一个类数组,用于保存实参的信息
通过arguments[index] 获取某一个参数
arguments.length 实参的个数
function add(){
if(arguments.length==0) return;
var sum=0;
for(var i=0,len=arguments.length;i<len;i++){
sum+=arguments[i];
}
return sum;
}
console.log(add());//undefined
console.log(add(1,2,3,4,5));//
arguments 是类数组,实质上还是对象
索引是数组下标,数字开头的变量名不合法,因此需要加引号
{
'0': 1,
'1': 2,
'3': 4,
length: 3
}
可以通过arguments来修改参数的值
function speak(m){
arguments[0]="";
return m;
}
console.log(speak("hh"));//空
arguments是每个函数中独有的,不会跨函数
function fn1(){
console.log(arguments);//Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
function fn2(){
console.log(arguments);//Arguments [2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
fn2(2);
}
fn1(1);
arguments.callee 指代函数本身
function add(){
console.log(arguments.callee);
}
add();
arguments.callee 常用于递归中
function factorial(num){
if(num==1) return 1;
return num*factorial(num-1);
}
console.log(factorial(5));//
function factorial(num){
if(num==1) return 1;
return num*arguments.callee(num-1);
}
console.log(factorial(5));//
不过在严格模式下,不允许使用arguments.callee(也不允许不使用var声明变量)
此时的解决方法就是将函数赋值给一个变量,这样函数本身的名字不会影响调用
"use strict";
var myfn=function factorial(num){
if(num==1) return 1;
return num*factorial(num-1);
}
console.log(myfn(5));//
实参的个数 arguments.length
形参的个数 函数名.length 或者arguments.callee.length
function add(n1,n2){
if(arguments.length != add.length) throw new Error("请传入"+add.length+"个参数!");
}
console.log(add(5));//Uncaught Error: 请传入2个参数!
什么做参数
1、没有参数
2、数字做参数
3、字符串( 如选择DOM节点,$("p") )
4、布尔值(保持函数的纯洁性,建议一个函数只做一件事情)
5、undefined(可选参数必须放在最后)
6、null
7、数组
$.each(["a","b","c"],function(index,item)){
console.log(index);//0 1 2
console.log(item);//a b c
}
8、对象
$.each({name:"cyy",age:24},function(index,item)){
console.log(index);//name age
console.log(item);//cyy 24
}
使用对象作为参数的好处(可以自由调换顺序)
function fn(obj){
var person={};
person.name=obj.name||"cyy";
person.age=obj.age||24;
person.tel=obj.tel||110,
person.addr=obj.addr||"China";
return person;
}
var cyy={
name: "cyy1",
age:25
}
console.log(fn(cyy));//{name: "cyy1", age: 25, tel: 110, addr: "China"}
9、函数
回调函数,如 setTimeout(fn, time);
函数的返回值
return:
表示函数结束
将值返回
什么可以做返回值:
直接return ,返回值是undefined
数字
字符串 :alert() 输出的都是字符串,会默认调用.toString() 方法
布尔值:常用于表单验证
null 和 undefined
数组
function add(n1,n2){
return [n1,n2,n1+n2];
}
console.log(add(5,6));//(3) [5, 6, 11]
对象
function fn(){
return {
name:"cyy",
age:25
}
}
注意return后面不要换行,否则默认是分号,到此结束;于是后面的会报错
function fn(){
//return会默认后面是分号,结束
return
{
name:"cyy",
age:25
}
}
函数
需要用()()来调用
document.write() 执行时会调用.toString() 方法,尝试将结果转换为字符串形式
document.write([1,2,3]);//1,2,3
document.write({
name:"cyy"
});//[object Object]
document.write({
name:"cyy",
toString:function(){
return "hh~";
}
});//hh~
function count(){
var num=1;
return function(){
return num++;
}
}
//每次调用count()时,num都会被初始化
//并且return num++ 是先返回num,再执行++
console.log(count()());//
console.log(count()());//
console.log(count()());//
//count()只执行了一次,因此num只初始化一次
//后面能够每次都进行递增+1
var fn=count();
console.log(fn());//
console.log(fn());//
console.log(fn());//
JS中函数的本质,定义、调用,以及函数的参数和返回值的更多相关文章
- C#调用存储过程带输出参数或返回值
CREATE PROCEDURE [dbo].[GetNameById] @studentid varchar(8), @studentname nvarchar(50) OUTPUT AS BEGI ...
- [转] ADO.NET调用存储过程带输出参数或返回值
CREATE PROCEDURE [dbo].[GetNameById] @studentid varchar(), @studentname nvarchar() OUTPUT AS BEGIN S ...
- Android调用远程Service的参数和返回值都需要实现Parcelable接口
import android.os.Parcel;import android.os.Parcelable; public class Person implements Parcelable{ pr ...
- JS基础语法---函数---介绍、定义、函数参数、返回值
函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...
- C#多线程函数如何传参数和返回值
详见网站:http://WWW.MOVIH.COM就是一个多线程爬虫系统. C#多线程函数如何传参数和返回值 提起多线程,不得不提起 委托(delegates)这个概念. 我理解的委托 ...
- Js中常用的字符串,数组,函数扩展
由于最近辞职在家,自己的时间相对多一点.所以就根据prototytpeJS的API,结合自己正在看的司徒大神的<javascript框架设计>,整理了下Js中常用一些字符串,数组,函数扩展 ...
- JS基础研语法---函数基础总结---定义、作用、参数、返回值、arguments伪数组、作用域、预解析
函数: 把一些重复的代码封装在一个地方,在需要的时候直接调用这个地方的代码就可以了 函数作用: 代码重用 函数的参数: 形参:函数定义的时候,函数名字后面的小括号里的变量 实参:函数调用的时候,函数名 ...
- C语言:将字符串中的字符逆序输出,但不改变字符串中的内容。-在main函数中将多次调用fun函数,每调用一次,输出链表尾部结点中的数据,并释放该结点,使链表缩短。
//将字符串中的字符逆序输出,但不改变字符串中的内容. #include <stdio.h> /************found************/ void fun (char ...
- javascript学习笔记(二):定义函数、调用函数、参数、返回值、局部和全局变量
定义函数.调用函数.参数.返回值 关键字function定义函数,格式如下: function 函数名(){ 函数体 } 调用函数.参数.返回值的规则和c语言规则类似. <!DOCTYPE ht ...
- 第4天:function对象(案例:获取当前日期属于当年第几天、arguments对象、函数类型、参数、返回值、自身调用)
获取当前日期输入当年第几天 //输入,年月日,获取这个日期是这一年的第几天 //年-月--日:20171月31日 function getDay(year,month,day){ //定义变量存储对应 ...
随机推荐
- python,读取txt的方法和应用
1.读取txt内的百度盘地址,循环保存到百度云中(直接访问下方地址) https://www.cnblogs.com/becks/p/11409467.html 2.读取txt内参数,循环执行查询,读 ...
- Django中model的class Meta
Class Meta 作用:使用内部类来提供一些metadata,以下列举一些常用的meta:1,abstract:如下段代码所示,将abstract设置为True后,CommonInfo无法作为一个 ...
- chrome浏览器无法开启同步功能 request cancel
解决办法 添加代理规则*.googleapis.com
- redis5.0 Cluster集群搭建
安装redis sudo apt update sudo apt install build-essential tcl cd ~ mkdir document/ cd document/ curl ...
- 微信小程序如何创建云函数并安装wx-server-sdk依赖
时间:2020/01/23 步骤 1.在微信开发者工具中云函数所在的文件夹的图标与其他文件夹是不同的,如下(第一个是云函数): 如果需要使一个普通文件变为云函数文件夹,需要在project.confi ...
- react 获取input的值 ref 和 this.setState({})
1.ref //class my_filter(reg){ const inpVal = this.input.value; console.log(inpVal) ...
- STM32系列之初探(二)
问题一: 什么是STM32 新的基于ARM内核的32位MCU系列 内核为ARM公司为要求高性能,低成本,低功耗的嵌入式应用专门设计的Crotex-M内核 标准的ARM体系 特点: 高性能 低电压 低功 ...
- 学习记录(安装Scala)
安装完spark之后根据教程安装Scala,在安装的时候提出警告,等了好长时间之后发现无法下载,最后搜索之后发现1.8版本的jdk无法安装,今天又重装了jdk换成了1.7.0的openjdk jdk安 ...
- html作业记录
<html> <head> <title>Hello World</title> </head> <body> <!-- ...
- 使用卷影拷贝提取ntds.dit
一.简介 通常情况下,即使拥有管理员权限,也无法读取域控制器中的C:\Windows\NTDS\ntds.dit文件.使用windows本地卷影拷贝服务,就可以获得该文件的副本. 在活动目录中,所有的 ...