一.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)。
哲学上讲,什么是对象?对象就是属性的无序集合。
对象的属性 的可以为一下特殊形式
- 特殊字符
- 数字
- 可以有空格
- 关键字、保留字,
先说一下,符合标识符规则的属性,可以使用点语法来访问属性了,也可以使用方括号:
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() 构造函数创建方法
构造函数创建方法由两种,
- 系统自带的构造函数 new Object()
系统自带的构造函数,就像工厂生产产品,长的都一样,但彼此独立,加上new操作符,就能给你返回一个对象,用变量接受对象
- 自定义的
遵循打驼峰式命名规则 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操作符,函数内部将会隐式执行三步
- 隐式的创建一个对象,var this = {}
- 执行代码
- 返回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方法由两层意思
- 函数执行
- 在函数执行的时候修改函数内部的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. 优先级
你需要做的就是找到函数的调用位置并判断应用了那条规则,如果某个调用位置可以应用多条规则,我们就需要通过优先级来区分
-
默认的优先级最低
-
显示优先级高于隐式
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
-
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
-
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绑定是为了提前传一些参数进去
判断原则
- 函数是否使用new操作符调用,如果有麻麻this将绑定到新创建的对象
- 函数是否使用call,apply或者硬绑定,如果有,this执行显示绑定的对象
- 函数是否在某个上下文对象中调用,如果有this指向那个上下文对象(隐式绑定)
- 如果以上都不是,那就是默认绑定,
4. 特殊情况
被忽略的this
如果你传入null或undefined将会忽略传入的值,实际使用默认值
那么什么时候会需要用到传入null的情况呢
使用apply展开参数
function foo(a,b){
console.log("a: "+ a + " ,b: " + b);
}
foo.apply(null,[2,3]);
因为这里我们不需要关心this指向,那么我们用null占位最好,