js函数

一、javascript最常见的函数定义方式:
1、函数声明,使用function语句
function print(x,y){
return x*y;
}
2、函数直接量定义函数
var print = function(x,y){
return x*y;
}
3、Function构造函数  new Function();
var print = new Function('a','b','return a+b;');

二、javascript对象创建
1、对象直接量/字面量
var obj = {
name:'li',
age:18,
sex:'nv'
}
console.log(obj.name);   //li

2、Function构造函数
(1)、系统自带的,eg: new Object(), Array(), Number(),Boolean(), Date()...
var obj = new Object();
obj.name = 'yue';
obj.age = 18;
obj.sex = 'nv';
console.log(obj.name);
(2)、自定义的,为了和普通函数区分,首写字母大些,采用驼峰式写法(普通函数采用小驼峰式写法)
function Obj(name){
this.name = name;
this.age = 18;
}
function Obj2(sex){
this.sex =  sex;
}
var obj = new Obj('wu');
var obj2 = new Obj2('nv');
console.log(obj.name);
console.log(obj.age);
console.log(obj2.sex);

用new和不用new返回的结果大相径庭。不用new,则Obj('lyl')根本就是一个函数的正常执行,没有返回值,则默认返回undefined,而是用new操作符后js引擎就会将该函数看作构造函数看待,经过内部的一系列隐士操作,返回值就是一个对象了。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { 
console.log(this.name)

}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');
console.log(person1);     
//person{name:"Zaxlct",age:28,job:"Software Engineer",sayName:f}
console.log(person2);     
//person{name:"Mick",age:23,job:"Doctor",sayName:f}
console.log(person1.constructor == Person); //true
  console.log(person2.constructor == Person); //true

  person1 和 person2 都是 构造函数 Person 的实例 
一个公式:实例的构造函数属性(constructor)指向构造函数。

三、对象的增、删、查、改
1、增加:所谓增添一个对象属性,就是直接对该属性进行赋值操作即可,这就相当于为该对象添加了一个新的属性,而打印未添加的属性,浏览器不会报错,而是会打印出undefined.
var obj4 = {};
console.log(obj4.name);  //undefined
obj4.name = 'xia';
console.log(obj4.name);  //xia

2、删:我们通过delete操作符来删除一个对象的属性
var obj = {
name:'xia'
}
console.log(obj.name);   //xia
delete obj.name;
console.log(obj.name);   //undefined

3、改:修改一个对象的属性,直接通过赋值操作赋予其其他的值即可
var obj = {
name:'xia'
}
console.log(obj.name);   //xia
obj.name = 'yun';
console.log(obj.name);   //yun

4、查:查询一个对象的属性值
var obj = {
name:'xia';
}
第一种方法:
console.log(obj['name']);     //xia
第二种方法:
console.log(obj.name);     //xia
PS:最本质的是第一种方法,因为在使用第二种方法时,后台自动将其转换为第一种字符串的形式来查询

PS:以上的增、删、改、查三种操作都只是针对当前对象的属性进行操作,而不会影响到当前对象的原型属性。
而查询是先看看当前对象本身是否设置了该属性,如果当前对象未设置该属性,则再看该对象的原型中是否设置了该属性,若两者都没有,则返回undefined。

四、包装类
1、五个原始值:number,string,Boolean,undefined,null。其中number,string,boolean是分别拥有自己的包装类,而undefined和null是没有自己的包装类。
2.原始值不是对象,无法拥有自己的属性,但因为包装类的存在,原始值就好似可以拥有自己的属性了,但其拥有的属性又有点特殊之处。

五、原型(prototype)
1、原型的定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过改构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
2、利用原型特点和概念,可以提取共有属性。将一类对象的共有属性提取出来,放到该类对象的原型中,从而不需要每次用new操作符时都重新定义一遍该共有属性。
 所有的函数对象都继承制原始函数对象;Function比较特殊,他的原型对象也就是原始函数对象;所以我们往往用Function.prototype表示原始函数对象;而原始函数对象又继承自原始对象。

六、原型链
1、定义:顾名思义,原型链就是将一个个原型串连起来,形成一条原型继承的链子。
2、原型链的构成:
如下代码例子, Child继承Parent, Parent继承GrandParent, 而GrandParent没有自定义原型,所以默认为原型链的最顶端new Object();
原型链的形成:
A.protorype = {
        name: 'a'
    }
    function A() (
        this.name = 'A';
    )
    B.prototype = new A();
    function B() {
        this.name = 'B';
    }
    C.prototype = new B();
    function C(){
        this.name = 'C';
    }
    C继承B,B继承A,形成C->B->A的一条以原型为继承方式的原型链

3、原型链的增删改查:

(1)查:优先去实例对象上寻找是否有该属性,若没有,则再去其对应的原型上去寻找该属性,若都没有,则返回undefined   
(2)增:直接给实例对象增添属性,则仅仅在实例对象上增添属性,若在原型上增添属性,则在原型上增添属属性,则在原型上增添属性,其实例继承原型增添的属性。
(3)删:delete仅仅能删除实例对象的属性,即构造函数原有的自己的属性和后来实例对象增添的属性,还有关于delete的一点需注意,delete无法删除原型的属性和用var定义的变量(即非window的属性)
(4)改:更改实例对象的属性,则仅仅更改实例对象的属性值;更改原型的属性值,则更改原型的属性值,继承该原型的对象对应属性值也会被更改;
ps.理解了在原型链上的增删改查后,自然就能理解在原型上的增删改查了,只要把在原型上的增删改查当成只有一个原型的很短的原型链即可。

七、对象继承
1、原型继承:
1)、说起原型继承,就要先由构造函数创造对象说起,首先了解构造函数内部基本原理:
(1).在函数体最前面隐式的加上this = {}
(2).执行 this.xxx = xxx;
(3).隐式的返回this

并且要注意隐士创建的this对象中有个名为__proto__的属性,其属性值为该构造函数继承的原型prototype。
而原型对象的有一个名为constructor的属性,其属性值为继承之的构造函数,所以可以通过这两个属性相互查看
2)、原型的定义及一些特点:
a.定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
b.利用原型特点和概念,可以提取共有属性。
c.对象如何查看原型 — > 隐式属性 __proto__
d.对象如何查看对象的构造函数 — > constructor

3)、若一个构造函数没有指定其原型,则其原型默认为Object.prototype

2、对象继承的发展
1)、传统模式:即正常的通过构造函数创建实例对象,来继承原型
缺点:继承了过多不必要的属性
2)、借用其他构造函数(即通过call / apply来改变构造函数内this对象的引用)
缺点:无法继承借用构造函数的原型

案例:
A.prototype = {
        name: 'a',
        age: 18,
        class: 1
    }
    function A(){
        this.name = 'A';
    }
    function B(){
        A.call(this);
    }
    var b = new B();
    console.log(b.name); // A
    console.log(b.age) // undefined

3)、共享原型:即将其他构造函数的原型直接赋值给本构造函数的原型
缺点:两个原型会想回影响,更改其中一个原型,更一个对应的原型也会被更改。
案例:
A.prototype = {
        name: 'a',
        age: 18,
        class: 1
    }
    function A(){
        this.name = 'A';
    }
    B.prototype = A.prototype;
    function B(){
        this.name = 'B';
    }
   C.prototype = A.prototype;
   function C() {
     this.name = "C";
   }


    var a = new A();
    var b = new B();
    var c = new C();
    console.log(a.age); // 18
    console.log(b.age); //18
    console.log(c.age); //18

    // 原型继承成功
    B.prototype.age = 20; //更改其中一个原型的age属性
    console.log(b.age);//20
    console.log(c.age); //20
    // 继承A原型的B和C相互影响
4)、圣杯模式:
每次继承的都是新创建的F构造函数实例,相互之间不会影响。其实此处针对F形成了闭包,Child引用了F,导致F不会销毁。
(1)正常函数形式:
function inherit (Child, Parent) {
    // 借用F这个中间量来继承,而不是直接共享原型
    var F = function (){}
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    // 自定义构造函数原型时,同时要更正自定义原型的constructor,否则一般默认为Object(),次函数若不指定constructor,则constructor为Parent
    Child.prototype.constructor = Child; 
    
    Child.prototype.uber = Parent; //记录真正继承的是谁
}

(2)、闭包形式
var inherit = (function(){
    var F = function (){};
    return function (Child, Parent) {
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        Child.prototype.uber = Parent;
    }
})();

八、命名空间
我们可以利用对象创建命名空间来管理变量,防止污染全局,适用于模块开发,如下简单的小demo:
var workSpace = {
    person1: {
       name: 'one',
       age: 18
    },
    person2: {
       name: 'two',
       age: 20
    }
}
// 这样两个人虽然有同名变量,但不会相互影响,因为位于不同命名空间
// 访问第一个人的姓名
console.log(workSpace.person1.name); // one
console.log(workSpace.person2.name); //two

九、实现类似jQuery中的链式调用:return this

函数定义
三种函数定义方式。
(1)、函数声明语句,使用function关键字,后跟一组参数以及函数体
如下小demo,其中的this不懂的没关系,上面会说,你只要把次demo中的this当成person对象就行
var person = {
  foodCount: 10,
  eat: function () {
      this.foodCount--;
      return this;
  },
  buy: function () {
      this.foodCount++;
      return this;
  },
  print: function () {
      console.log(this.foodCount);
  }
}
// foodCount初始值为10, 在连续吃了三次后,变为7
person.eat().eat().eat();
person.print(); //7

十、对象的枚举:
1.obj.hasOwnProperty('prop');
该方法的作用是来判断对象obj的自身属性中是否含有属性prop,自身属性是在构造函数中生成的或者实例对象后来自己添加的,而继承属性则是从原型上继承的属性,所以该方法就是判断该属性是从原型继承来的还是自身的。
作用: 遍历一个对象的所有自身属性,因为es5中的对象遍历是默认打印包括继承自原型的属性的,demo如下
Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
//  未用hasOwnProperty
// print:
//  lyl
for(var prop in person) {
console.log(person[prop]);

//  使用hasOwnProperty
// print: 
// 18 lyl
for(var prop in person) {
if(person.hasOwnProperty(prop)) {
console.log(person[prop]);
}
}
2、prop in obj; 
in操作符用来判断该对象obj上是否有该属性prop,prop既可以是自身属性,也可以是继承属性,如下demo
Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
console.log('age' in person); // true
console.log('name' in person); //true
delete person.name;
console.log('name' in person); //false
  3、object instanceof Object;
instanceof操作符用来判断object实例对象是否为Object构造函数创建的,如下demo
Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
console.log(person instanceof Person); // true
console.log(new Object() instanceof Person); //false

十一、this基本介绍
1、函数预编译过程 this —> window
2、全局作用域里 this —> window
3、obj.func();   func()里面的this指向obj), 可以这样理解,谁调用func,则this就指向谁
4、call/apply 可以改变函数运行时this指向,
(1)、call用法:
func.call(要改变后的this, arg1, arg2, ... );
(2)、apply用法:
func.apply(要改变后的this, [arg1, arg2, arg2]);
(3)、apply和call共同点:都是改变this指向
apply和call不同点:传参形式不同,call是将参数一个个传进来,而apply是将所有参数存进一个数组中,然后将该数组传
如下demo:
// demo1
function demo1() {
console.log(this);
}
// demo1() <==> this.demo1(); <==> window.demo1()
demo1(); // window
// demo2
var demo2 = {
retThis: function () {
console.log(this);
}
}
demo2.retThis(); // demo2 = {...}
// call / apply改变this
demo1.call(demo2);  // demo2 = {}
demo2.retThis.call(window); // window

十二、对象的克隆
克隆的概念:
(1)、浅度克隆:原始类型为值传递,对象类型仍为引用传递
(2)、深度克隆:所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反应到原对象中。
浅克隆的表现:
(1)、原始类型
//数值克隆的表现
var a="1";
var b=a;
b="2";
console.log(a);// "1"
console.log(b);// "2"

//字符串克隆的表现
var c="1";
var d=c;
d="2";
console.log(c);// "1"
console.log(d);// "2"

//字符串克隆的表现
var x=true;
var y=x;
y=false;
console.log(x);// true
console.log(y);// false

从上面的代码大家可以看出,原始类型即使我们采用普通的克隆方式仍能得到正确的结果,原因就是原始类型存储的是对象的实际数据。

(2)、对象类型
前面说过,函数式一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现.
var m=function(){
alert(1);
};
var n=m;
n=function(){
alert(2);
};
 
console.log(m());//undefined
console.log(n());//undefined
这里定义一个复杂的对象类型oPerson.
var oPerson={
    oName:"rookiebob",
    oAge:"18",
    oAddress:{
        province:"beijing"
    },    
    ofavorite:[
        "swimming",
        {reading:"history book"}
    ],
    skill:function(){
        console.log("bob is coding");
    }
};
function clone(obj){
    var result={};
    for(key in obj){
        result[key]=obj[key];
    }
    return result;
}
var oNew=clone(oPerson);
console.log(oPerson.oAddress.province);//beijing
oNew.oAddress.province="shanghai";
console.log(oPerson.oAddress.province);//shanghai

通过上面的代码,大家能看到,经过对象克隆以后,我修改oNew的地址,发现原对象oPerson也被修改了。这说明对象的克隆不够彻底,那也就是说深度克隆失败!

深度克隆的实现:
为了保证对象的所有属性都被复制到,我们必须知道如果for循环以后,得到的元素仍是Object或者Array,那么需要再次循环,直到元素是原始类型或者函数为止。为了得到元素的类型,我们定义一个通用函数,用来返回传入对象的类型。
//返回传递给他的任意对象的类
function isClass(o){
    if(o===null) return "Null";
    if(o===undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8,-1);
}
PS:Object.prototype.toString.call(o)能直接返回对象的类属性,形如"[object class]"的字符串,我们通过截取class,并能知道传入的对象是什么类型。

深度克隆案例:
//深度克隆
function deepClone(obj){
    var result,oClass=isClass(obj);
    //确定result的类型
    if(oClass==="Object"){
        result={};
    }else if(oClass==="Array"){
        result=[];
    }else{
        return obj;
    }
    for(key in obj){
        var copy=obj[key];
        if(isClass(copy)=="Object"){
            result[key]=arguments.callee(copy);//递归调用
        }else if(isClass(copy)=="Array"){
            result[key]=arguments.callee(copy);
        }else{
            result[key]=obj[key];
        }
    }
    return result;
}
//返回传递给他的任意对象的类
function isClass(o){
    if(o===null) return "Null";
    if(o===undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8,-1);
}
var oPerson={
    oName:"rookiebob",
    oAge:"18",
    oAddress:{
        province:"beijing"
    },    
    ofavorite:[
        "swimming",
        {reading:"history book"}
    ],
    skill:function(){
        console.log("bob is coding");
    }
};
//深度克隆一个对象
//第一种方法、通过递归解析解决
var china = {
nation : '中国',
birthplaces:['北京','上海','广州'],
skincolr :'yellow',
friends:['sk','ls']
}
//深复制,要想达到深复制就需要用递归
function deepCopy(o,c){
var c = c || {}
for(var i in o){
if(typeof o[i] === 'object'){
//要考虑深复制问题了
if(o[i].constructor === Array){
//这是数组
c[i] =[]
}else{
//这是对象
c[i] = {}
}
deepCopy(o[i],c[i])
}else{
c[i] = o[i]
}
}
return c
}
var result = {name:'result'}
result = deepCopy(china,result)
console.dir(result)

// 第二种方法:通过JSON解析解决
var test ={
name:{
xing:{ 
first:'张',
second:'李'
},
ming:'老头'
},
age :40,
friend :['隔壁老王','宋经纪','同事']
}
var result = JSON.parse(JSON.stringify(test))
result.age = 30
result.name.xing.first = '往'
result.friend.push('fdagldf;ghad')
console.dir(test)
console.dir(result)

var oNew=deepClone(oPerson);
 
oNew.ofavorite[1].reading="picture";
console.log(oNew.ofavorite[1].reading);//picture(克隆后)
console.log(oPerson.ofavorite[1].reading);//history book(克隆前)
 
oNew.oAddress.province="shanghai";
console.log(oPerson.oAddress.province);//beijing(克隆前)
console.log(oNew.oAddress.province);//shanghai(克隆后)
从上面的代码可以看到,深度克隆的对象可以完全脱离原对象,我们对新对象的任何修改都不会反映到原对象中,这样深度克隆就实现了。

这里要注意一点的就是:为什么deepClone这个函数中的result一定要判断类型?这里有一种情况,如果你的result直接是{}对象,我明明传进去的是一个数组,结果你复制完了以后,变成了一个对象了。
//深度克隆
function deepClone(obj){
    var result={},oClass=isClass(obj);
    // if(oClass==="Object"){
    //     result={};
    // }else if(oClass==="Array"){
    //     result=[];
    // }else{
    //     return obj;
    // }
    for(key in obj){
        var copy=obj[key];
        if(isClass(copy)=="Object"){
            result[key]=arguments.callee(copy);
        }else if(isClass(copy)=="Array"){
            result[key]=arguments.callee(copy);
        }else{
            result[key]=obj[key];
        }
    }
    return result;
}
function isClass(o){
    if(o===null) return "Null";
    if(o===undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8,-1);
}
//克隆一个数组
var arr=["a","b","c"];
var oNew=deepClone(arr);
console.log(oNew);//Object {0: "a", 1: "b", 2: "c"}

转载:https://www.cnblogs.com/libin-1/p/5911190.html
转载:https://www.cnblogs.com/jq-melody/p/4499333.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值