JS函数深入讲解-1

1.什么是对象
对象是任意值的集合

var cat = {
	'name':'Tom',//名值对||键值对--属性
	'age':4,
    'family':['father','mom'],
    'speak':function(){
    	console.log('mm');
    },//属性之间不能缺少逗号!
    'friend':{
    	'name':'Tom',
		'age':4
    }
};//字面量创建方式

2.如何使用对象
创建方式

var cat = new Object();//构造函数
var cat = {};//字面量
Object.create();//只有ES5适用

读写

cat.name = 'Tom';
cat['name'] = 'Tom';//不能缺少引号
cat.type = 'dd';
console.log(cat.name);
console.log(cat['name']);

删除属性

delete cat.name/cat['name'];
console.log(cat.name);//没有值的属性是undefined,不会报错

检测是否有某个属性

console.log('type' in cat);//要有引号

枚举

for(var p in cat){
	console.log(p);
	console.log(cat[p]);//p代表属性名,它是带有引号的字符串
}

点语法只能跟字符串,方括号语法可以支持更复杂的运算

补充:for…in 语句用于遍历数组或对象的属性,也就是说,for…in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。

for(变量 in 对象){
	执行代码
}

3.函数
什么是函数:一次封装,四处使用
通过函数名确定函数的位置,再进行调用;没有名字的函数称为匿名函数
JS的参数并不限制传入个数,可以一对多
函数体执行到return语句结束

定义函数时,函数作为window的方法存在;调用函数时,函数内部生成一个局部作用域进行计算;函数执行完毕后销毁局部作用域与局部变量,但函数本身还存在…每一次执行都重复上述步骤

函数的优势:复用代码(自己的或他人的)、统一修改和维护、增加程序的可读性(不应该暴露过多的细节、函数命名应该有意义)–读代码时也应该抓主要函数,略过不重要的细节

函数的二象性:可调用—函数也是对象

//字面量定义方式
function add(num1,num2){
	...
}
//构造函数定义
new Function('num1','num2','...')
function add(num1,num2){
	return num1 + num2;
}
add.sex = 'male';//可以为函数设置属性和方法,且不影响函数正常的功能
add.setSex = function (sex){
	this.sex = sex;
}

函数可以作为数据值使用(匿名函数);函数名也可以当做变量来对待

var add = function(){...}//变量与函数本体对应
add();

数组与对象内都可以有函数

函数作为参数使用:

setTimeout(fn);//函数名对应函数本体,可作为参数,若带有()则会执行函数
function fn(){console.log(1);}

函数作为返回值:

function fn(){
	return function (){
		console.log(1);
	}
}
var newFn = fn();//将返回的匿名函数赋值给变量
newFn();
//或者
fn()();//注意只能用在以函数为返回值的情况!

4.函数的定义方法
字面量(function声明/var的赋值表达式语句)–构造函数(参数和函数体都以字符串方式输入)

var add = new Function('num1','num2','return num1 + num2');
add();

构造函数的效率比字面量低;声明不要分号,语句必须带分号

console.log(add());
var add = function(){
	return 1;
};

console.log(add());
function add(){
	return 1;
}

预解析会造成声明提前
第一种情况由于预解析时add被认为是变量,定义为undefined,所以会报错;第二种情况预解析时add被声明为函数,所以会调用函数
构造函数方式一般不使用,另外两种方式基本等价

5.函数定义的位置
1)全局作用域:哪里都可以调用
2)函数作用域:定义的函数内、自身内部、同级函数内部都可以调用
3)if/for的代码块内部:JS中没有块级作用域,只有作用域才能进行预解析,所以在预解析时,定义在代码块内部的函数相当于定义于全局作用域,但可以如下操作:

if(true){
	var add = function(argument){
		...
	}
}

预解析时认为add为全局变量,赋值undefined,运行到该步骤时将函数赋值给add,不推荐,尽量避免在代码块中定义函数!
4)对象中的函数:方法,调用时需采用对象的方式

var person = {
	name:'xm',
	setSex:function(sex){
		this.sex = sex;
	}
}
person.setName = function(name){
	this.name = name;
}
person.setName('xh');

5.普通函数的调用
1)命名函数:函数名();
2)匿名函数:

var add = function(){};//相当于命名函数
add();//函数名+()
//直接调用
function(){...}();//函数本体+()
//正确方法
(function(){...})();
(function(){...}());
!function(){...}();
+function(){...}();
-function(){...}();
console.log(function(){...}());
//(函数本体)+(),匿名函数自执行

由于函数名是指向函数本体的指针,所以是等价的;但由于JS解析器将function关键字视作函数声明,并不能对其进行执行,所以第二种会报错,需要用()将函数本体包裹,或者用其它符号开头。
匿名函数若有参数,在调用时直接传入。

(function(num1,num2){
	return num1-num2;
})(55,23);

3)递归调用

function factorial(num){//计算阶乘
        if(num == 1) return 1;//不可或缺,必须要有!
        return num * factorial(num-1);
    }
console.log(factorial(6));

递归有两个要点:一是运算可以通过相同的方式进行简化,以达到调用自身就能计算的目的;二是运算有一个最小不可分的元素,递归到此元素为止,所以必须要有返回最小不可分元素值这一步!

6.方法的调用
1)对象名.方法名();-方法也是特殊的属性
2)事件调用,事件本质上也是一个方法

document.onclick = function(){
	console.log(...);
};//点击时会调用
document.onclick();//也可以自己调用,用以模拟事件

3)不合法方法调用
对象里的属性名如果符合变量命名规则,可以不加引号,用‘.’来调用;如果不合法必须加引号,调用时采取方括号方式!

var operation = {
	'@':function(){
		console.log('@');
	},
	add:function(){
		...
	}
}
console.log(operation['@']());
console.log(operation['add']());

可以通过[“方法/属性名”]的方式(要有引号)或者传入值为方法/属性名的变量的方式调用(不要引号)

4)链式调用

var operation = {
	sub:function(){
		console.log('@');
		return this;
	},
	add:function(num1,num2){
		console.log(num1+num2);
		return this;
	}
}
operation.sub().add(1,2);//只有opration才可以调用方法,所以要返回this

链式调用只能用点串联,不能出现中括号,且前一个方法要带()才能执行并返回this

6.构造函数的调用

function Person(){//一般首字母大写
	...
}
new Person();//构造函数要用new的方式调用

构造函数返回一个对象

内置构造函数

new Object();
new Array();

7.函数的间接调用

//每个函数都有call与apply方法,它们包含两个参数
var name = 'xm';
var person = new Object();
person.name = 'xh';
person.getName = function(){
	return this.name;
}
console.log(person.getName.apply());
console.log(person.getName.call(window));//若无参数,两个方法都能间接调用函数,第一个参数能够改变this指向的位置

第一个返回xh,第二个由于改变了this的指向,返回xm;

function add(num1,num2){
	return num1 + num2;
}
console.log(add.call(window,1,2));//没有this,无影响
console.log(add.apply(window,[1,2]));
var datas = [3,2];
console.log(add.apply(window,datas));

call方法传参时一个一个的传入,apply则利用数组来传入参数!不管采用哪种方式传入参数,函数内部都是一个类数组对象arguments接收,所以没有差别。

8.函数的参数
1)参数的类型

function add(num1,num2){//形参,相当于局部变量,只是占位符
	return num1+num2;
}
add(1,2);//实参,传入后代替形参

参数传递的本质是将实参赋值给形参
当传递的实参是基本类型时,相当于将值拷贝给形参,对形参的修改不会影响实参;如果传递的是引用类型,则是将地址拷贝给形参,形参实参都指向同一个对象,修改一个另一个也发生变化。

var person = {};
function setPerson(obj){
	obj.name = 'xm';
}
setPerson(person);

2)参数的个数
1.实参个数与形参个数相等
2.实参个数少于形参个数,多出的形参预解析时等于undefined-有可选参数时,有默认值,可选参数位置不要放在前面

function pow(base,power){
	power = power || 2;//短路操作,如果power为undefine,则赋值为2
	return Math.pow(base,power);
}
console.log(pow(3,3));
console.log(pow(3));//采用默认参数

3.实参个数多于形参个数

//当不知道设置多少形参时,干脆一个形参都不写,利用arguments
function add(){
	if(arguments.length == 0) return;
	var sum=0;
	for(var i=0;i<arguments.length;i++){
		sum += arguments[i]; 
	}
	return sum;
	}

3)arguments
每一个函数都有的类数组对象,有length属性和[…]调用方式

//arguments其实是对象
{
	'0':1,
	'1':2,
	'2':3,
	length:3
}

arguments内的数据和形参一一对应,函数中不要随便修改删除arguments内的数据;arguments是每一个函数所独有的,不会跨函数

arguments.callee 指代函数本身,一般用于递归调用
function factorial(num){//计算阶乘
        if(num <= 1) return 1;
        return num * arguments.callee(num-1);//避免对函数名的修改带来错误
    }

但在严格模式下,arguments.callee不被允许使用,可使用如下方式

var jiecheng = function fn(){
	if(num <= 1) return 1;
    return num * fn(num-1);//fn相当于函数的别名,只能在内部使用
}

每一个函数都有length属性,它表示形参的个数,可用于比较实参形参是否匹配

function add(num1,num2){
	if(arguments.length != add.length){
		throw new Error('请传入'+add.lenth+'个参数!')
	}
	return num1+num2;
}
<body> 
   <p id="test" style="background-color: red; color: blue;">我是一个段落!</p>
<script type="text/javascript"> 
    function css(){
        if(arguments.length == 2){
            return arguments[0].style[arguments[1]];
        }else if(arguments.length == 3){
            arguments[0].style[arguments[1]]=arguments[2];//style相当于一个对象
        }
    }
    var p = document.getElementById('test');
    css(p,'background-color','orange');
    console.log(css(p,'color'));
</script> 
</body> 

补充:
1.当属性值用变量表示时,不能通过elem.style.attr来设置
2.当采取行内样式时,无法使用elem.getAttribute和elem.setAttribute
3.只能采取elem.style[attr]的方式来进行修改!

4)什么可以做参数
原则上所有数据值都可以作为参数,包括数字、str、Boolean、null、undefined、object、array(apply方法或下图方法)、函数(要习惯将函数作为参数-回调函数)

$.each({name:'xm',sex:'male'},function(index,item){
	console.log(index);//属性名
	console.log(item);//属性值
})//JQ中的用法,可以传数组或对象入函数

当函数的参数在三个或以上时,用对象作为参数可以避免参数顺序不匹配、可选参数占位的问题

function setPerson(obj){
	var person = {};
	person.name = obj.name || 'xh';
	person.sex = obj.sex || 'male';
	person.age = obj.age || '18';
	person.tel = obj.tel || '110';
	person.addr = obj.addr || 'China';
}//短路操作设置默认值
setPerson({
	name:'xm',
	age:'18',
	addr:'China',
	sex:'male'
});//用对象传入参数,顺序和参数个数无要求

9.函数的返回值
1)return.continue.break
return用在函数中,返回后结束函数
continue用在循环中,跳出本次循环
break用在循环中,跳出整个当前循环,只能跳出一层循环
2)什么可以作为返回值
什么都不返回相当于返回undefined、number、string、boolean(表单验证)、null、数组、对象、函数(一个函数只实现一个功能)

function fn(){
	return function(argument){
		console.log(1);
	}
}
fn()();

为了写出更好的函数,会将函数作为参数传到另一个函数中,组成一个新功能函数,此时这个函数有两个功能:传进来函数的功能和接收函数本身的功能
补充:alert()与document.write()期望接收字符串,弹出前自动调用toString()方法,一般对象调用toString()方法后返回[object Object],如果重写toString()方法,则按照重写后的输出

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值