分享学JavaScript的第七天

一.Object 类型

对象是变量的数据类型,什么数据类型呢 和函数一样是引用数据类型

// 描述心中的对象
var wuwei = {
    name: "wuwei",
    age: 18,
    sex: "male",
    eat: function (){
        console.log("I eat damifan")
    }
    
}

什么是对象?

对象是由一对或者多对属性名和属性值组成的,我们把属性名叫做键名,属性值叫做键值,所以对象也是由很多对键值组成的;

对象是引用数据类型,操作对象时实际是在操作对象的内存地址

对象的键名是字符串类型的,键值可以是任意数据类型

1. 对象的创建

创建一个对象,有两种方法,第一种比较简单,叫做字面量;第二种是用new Object()。

1.1 字面量的创建方式

plainObject 对象字面量/ 对象直接量

var obj = {
    name : "wuwei",
    age : 18,
    sex : "男"
};

console.log(obj);
console.log(obj.name);
console.log(typeof obj);

{}就是对象的界定符,就是对象的字面量。对象有属性,所谓的属性就是这个对象的特点、特性,name、age、sex都是这个obj对象的属性(preperty)。

哲学上讲,什么是对象?对象就是属性的无序集合。

对象的属性 的可以为一下特殊形式
  1. 特殊字符
  2. 数字
  3. 可以有空格
  4. 关键字、保留字,

先说一下,符合标识符规则的属性,可以使用点语法来访问属性了,也可以使用方括号:

var obj = {
	name : "考拉"
} 
console.log(obj.name)
console.log(obj["name"]);

特殊形式的属性,必须要加上引号,检索属性的时候,必须用方括号:

var obj = {
    "24&" : "哈哈",
    "all name" : "呵呵",
    "++%%%" : "嘻嘻",
    "var" : "么么哒",
    "function" : "嘿嘿"
} 

console.log(obj["24&"]);
console.log(obj["all name"]);
console.log(obj["++%%%"]);
console.log(obj["var"]);
console.log(obj["function"]);

你会发现,JS会一个对象的属性名,没有特殊的规定。这是因为属性名不是标识符,没有那些规定。

对象的属性的访问,点语法是有局限的,它不能访问上面的特殊的那些情况。也不能访问以变量保存的key:

var obj = {
    name : "考拉",
    age : 18,
    sex : "男",
    "study score" : 100
}

//console.log(obj."study score");//错误的
console.log(obj["study score"]);	//正确的
console.log(obj["a" + "ge"]);		//正确的
var a = "sex";
console.log(obj[a]);			//正确的
如果属性不是一个字符串会被转成字符串
var obj = {};
var a = {key:'a'};
var b = {key:'b'};
obj[a] = 123;
obj[b] = 345;
console.log(obj[a]);

这里变量a,b都是对象,转换为字符串都是[object Object]所以这里obj只有一个属性先设置为123,后修改为345

1.2 new 操作符创建对象

new Object() 构造函数创建方法

构造函数创建方法由两种,
  1. 系统自带的构造函数 new Object()

系统自带的构造函数,就像工厂生产产品,长的都一样,但彼此独立,加上new操作符,就能给你返回一个对象,用变量接受对象

  1. 自定义的

遵循打驼峰式命名规则 Person TheFirstName

var obj = new Object();	//这是一个空对象,里面没有任何属性
obj.name = "wuwei";
obj.age = 18;
obj.sex = "男";

console.log(obj);	
console.log(obj.age);	
console.log(typeof obj);

new是一个运算符,你没有看错,和±*/一样是一个运算符。表示新创建一个对象。一会儿我们学习构造函数。Object()大写字母O,这是一个系统内置的构造函数,

下面就可以用obj.key = value ;来追加属性了:

obj.name = "考拉";

事实上,工程师更喜欢用字面量的方式来创建对象。因为更直观:

对比一下

字面量方式:

var obj = {
    name : "考拉",
    age : 29,
    sex : "母"
};

构造函数:

var obj = new Object();
obj.name = "考拉";
obj.age = 30;
obj.sex = "男";

上面两种方式创建出来的对象,是相同的。字面量的方式直观、简单、并且有“封装”的感觉。所以我们鼓励大家用字面量来创建对象。

但是,不不建议杂糅使用:

var obj = {};   //的确能创建一个空对象
obj.name = "考拉";   //追加属性
obj.age = 18;        //追加属性
obj.sex = "男";      //追加属性
2. 对象的属性值

对象属性值,可以是任何东西。比如数字、字符串、布尔值、正则表达式、对象、数组、函数……

var kaola = {
    name : "考拉",
    age : 22,
    peiou : {
        name : "母考拉",
        age : 37
    }
}
var obj = {
    a : 1,
    b : "哈哈",
    c : true,
    e : function(){
        console.log(1+2);
    },
    f : {
        p : 1
    }
}

特别的,当对象的属性的值是一个函数的时候,我们称这个函数是对象的方法。

3. 对象的方法

当一个对象的属性的值,是一个函数,那么这个函数我们就称为对象的“方法”(method)。

方法就是一个对象能够做的事情,一般来说,就是一个动词。比如小明打招呼、长大、变性方法。

var xiaoming = {
    name : "小明",
    age : 18,
    sex : "男",
    sayHello : function(){
        alert("你好,我是" + xiaoming.name);
        alert("今年" + xiaoming.age + "岁了");
        alert("我是可爱的小" + xiaoming.sex + "生");
    }
}

xiaoming.sayHello();

比如上面的案例,sayHello就是一个属性,只不过它的值是一个函数,所以我们就可以说xiaoming这个对象,有sayHello方法。

说白了,我们学习“方法”,无非就是学习了一种函数的调用方式。这个函数里面的this指的是这个对象。

函数里面的this到底是谁,在函数定义的时候并不知道,要看函数如何被调用。

对象的方法的哲学,就是操作自己的属性。如果一个对象的方法,不操作自己的属性,那干嘛还要是方法呢?

zhangda方法,就是让自己的age++:

var xiaoming = {
    name : "小明",
    age : 18,
    sex : "男",
    sayHello : function(){
        alert("你好,我是" + this.name);
        alert("今年" + this.age + "岁了");
        alert("我是可爱的小" + this.sex + "生");
    },
    zhangda : function(){
        this.age++;
    }
}
4. 对象的特点

对象的属性名是字符串类型的,当属性名不是字符串时,会把属性名隐式转换成字符串类型,调用toString()方法
对象的属性名不能重复

5. 对象的常用操作

增加 obj.height = ‘1.8m’
删除 delete obj.name
改变 obj.name =‘xuanwu’
查看 obj.age

5.1 对象操作属性名的两种方式

点操作

obj.name 

[]操作

obj[“name”] 	//  obj.name
obj[name]	
6. 对象的遍历

for-in循环 作用:遍历对象

var obj = {
    name: "wuwei",
    age:18,
    sex: "男"
}
for(var key in obj){
    console.log(key);
    consol.log(obj.key);
}

上面程序输出的结果是什么?

二.认识构造函数

构造函数内部原理,构造函数必须加new操作符,本来他只是一个普通的函数,加new就能产生构造函数的功能,构造函数的功能就是创建对象

一旦使用new操作符,函数内部将会隐式执行三步

  1. 隐式的创建一个对象,var this = {}
  2. 执行代码
  3. 返回this对象
function Person(name,age){
    // var this = {}
    this.name = name;
    this.age = age;
    this.sex = "男";
    this.say = function(){
        console.log(this.name);
    }
    // return this
}
var student = new Person("wuwei",18)

题目:

function Person(name,age){
    var a = 0;
    this.name = name;
    this.age = age;
    function student(){
        a ++;
        console.log(a);
    }
    this.say = student;
}

var oPerson = new Person();
oPerson.say();   // 1
oPerson.say();   // 2
var oPerson2 = new Person();
oPerson2.say();   // 1

三. 包装类

1. 数字 字符串 布尔值的包装类

基础数据类型是不能有属性和方法的大家知道吧,属性和方法只有对象有,是对象特有的,像Function,Object,Array 都是对象,原始值不能算到对象里面的,他只是一个值,作为一个独立的个体存在

数字都是原始值对吗,不对,数字分两种,只用原始值数字才是原始值

var num = 123;            // 这是原始值数字
var num2 = new Number(123); // 这也是数字,这很明显是构造函数,然后new出来的对象,
console.log(num2);  // Number {123} 返回对象形式的123

一旦变成对象,这家伙就飞了,可以有属性可以有方法

num2.name = "wuwei";

同时它还是具有原来值的特性

var aa = num2 * 2;
console.log(aa);     // 246

字符串,布尔值完全一样

var str = new String("wuwei");
var bol = new Boolean(true);
2. undefined null 没有包装类

所以不能像对象一样是用属性和方法

undefined.name = "aa";
// 报错,报引用类型错误

现在明白了吧字符串,布尔值,数字有两种,原始值是没有属性和方法的,另外一种对象,是有属性和方法的

3. 作用
var str = "wuwei";
console.log(str.length);  //5

不是说原始值没有属性和方法吗,你这里的length是哪里来的 很明显是在操作属性啊

我们在看下能不能给他设置属性

var str = "wuwei";
str.name = 123;

console.log(str.name);  // undefined
var str = "wuwei";
str.name = 123;   
// 隐式的 new String(str);

console.log(str.name);  
// 再次new Sting(str);这个和上面不是一个String,这个对象上没有name

基于这样一个理论,

var str = "wuwei";
str.length = 2;
console.log(str);

这个就是包装类,如果你发现原始值操作属性都是被隐式的包装成为了对象

四.this指向问题

1. this 指向问题

this关键字是JS中最复杂的机制,它是一个很特别的关键字,被自动定义在所有函数的作用域中.

function foo(){
	console.log(this)
}
foo();

this指向的是一个对象,我们把this指向的对象叫做函数执行的上下文对象

当函数被调用的时候,this指向会发生改变,指向调用函数的对象

在函数预编译阶段,会生成AO对象,程序还会默认把this作为AO对象的一个属性名,默认属性值是window

2.this绑定的规则
2.1 默认绑定

就是直接使用不带任何修饰的函数引用进行的调用,就是默认绑定,无法应用其他规则

function fn(){
    console.log(this);
}
fn();   

// 改变
function fn(){
    console.log(this.a);
}
var a = 2;
fn();
2.2 隐式绑定

考虑函数调用位置是否有上下文对象,或者说是否被某个函数拥有或包含

示例:

function fn(){
    console.log(this);
}      
var obj = {
    a : 33,
    fn:fn
}
obj.fn();       // obj

对象引用链只有上一层或者说最后一层在调用的位置中起作用

function foo(){
    console.log(this.a);
}
var obj2 = {
    a: 42,
    foo: foo
}
var obj1 = {
    a: 22,
    obj2: obj2
}
obj1.obj2.foo();   // 42

隐式绑定的函数会丢失绑定对象

function foo(){
    console.log(this.a);
}
var obj = {
    a : 20,
    foo: foo
}
var a = 33;
var bar = obj.foo;
bar(); //33
// 此时赋值的知识函数的引用,bar则是一个不带任何修饰的函数调用,因此应用了默认绑定
2.3 显示绑定

我们发现隐式绑定时,必须在一个对象内部包含一个指向函数的属性.并通过这个属性间接的应用函数.从而把this隐式的绑定到这个对象上.

如果我们不想在函数内部包含函数的引用呢.我们就只能用接下来要学习的方法,显示的绑定了

call && apply方法,

作用.都是在函数执行时,修改this的指向

call 和 apply方法由两层意思
  1. 函数执行
  2. 在函数执行的时候修改函数内部的this指向

使用

function foo(){
    console.log(this.a)
}
var obj = {
	a: 20
}
foo.call(obj);

这里就是通过call这个方法子foo函数调用的时候将函数内部的this绑定到了obj上

如果我传入一个数字,字符串 布尔值怎么办

function foo(){
    console.log(this.a)
}
var obj = {
	a: 20
}
foo.call(true);
// 为什么是undefined 而不报错呢

这是包装类,我们一会将,但是我们要知道,这里第一参数必须是对象,如果不是对象,会将其转为包装对象,转不了就是window

call && apply 方法的区别

fn.call(obj,attr1,attr2,attr3,..);
fn.call(obj,[attr1,attr2,attr3,..])

这两个方法传参的方式不同,apply只能传数组

示例:

function add(c,d){
    return this.a + this.b + c + d;
}
var s = {a:1,b:2};
console.log(add.call(s,3,4));
console.log(add.apply(s,[5,6]));

上面程序的输出结果是什么?

硬绑定

function foo(){
    console.log(this.a);
}
var obj = {
    a: 35
}
var bar = function(){
	foo.call(obj);
}

bar();
bar.call(window);

因为硬绑定非常常用的方法,所有ES5也提供了内置的方便.bind

function foo(){
    console.log(this);
    console.log(this.a) ;
}
var obj = {
    a: 23
}
var bar = foo.bind(obj)
bar();

此时就算你使用call,apply 也改变不了函数的bar的this绑定

function foo(){
    console.log(this);
    console.log(this.a) ;
}
var obj = {
    a: 23
}
var a = 33;
var bar = foo.bind(obj)
bar.call(window);
2.4 new 操作符

这个我们讲构造函数的的时候子细聊,先知道关于this指向的绑定情况有四种

3. 优先级

你需要做的就是找到函数的调用位置并判断应用了那条规则,如果某个调用位置可以应用多条规则,我们就需要通过优先级来区分

  1. 默认的优先级最低

  2. 显示优先级高于隐式

    function foo(){
        console.log(this.a)
    }
    var obj1 = {
        a: 10,
        foo: foo
    }
    var obj2 = {
        a: 20,
        foo: foo
    }
    obj1.foo();    // 10
    obj2.foo();    // 20
    
    obj1.foo.call(obj2);  //  20
    obj2.foo.call(obj1);  //  10
    
  3. new绑定比隐式绑定优先级高

    function foo(aa){
        this.a = aa
    }
    var obj = {
    	foo.foo
    }
    obj.foo(2);
    console.log(obj.a);    // 2
    
    var bar = new obj.foo(4);
    console.log(obj.a);    // 2
    console.log(bar.a);    // 4
    
  4. new操作符比显示绑定中的硬绑定优先级高

    function foo(aa){
        this.a = aa;
    }
    var obj = {}
    var bar = foo.bind(obj);
    
    bar(20);
    console.log(obj.a);
    
    var baz = new bar(30);
    console.log(obj.a);
    console.log(baz.a);
    

    至于为什么用new操作符还要用bind绑定是为了提前传一些参数进去

判断原则
  1. 函数是否使用new操作符调用,如果有麻麻this将绑定到新创建的对象
  2. 函数是否使用call,apply或者硬绑定,如果有,this执行显示绑定的对象
  3. 函数是否在某个上下文对象中调用,如果有this指向那个上下文对象(隐式绑定)
  4. 如果以上都不是,那就是默认绑定,
4. 特殊情况

被忽略的this

如果你传入null或undefined将会忽略传入的值,实际使用默认值

那么什么时候会需要用到传入null的情况呢

使用apply展开参数

function foo(a,b){
    console.log("a: "+ a + " ,b: " + b);
}
foo.apply(null,[2,3]);

因为这里我们不需要关心this指向,那么我们用null占位最好,

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oBj-小飞猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值