对象
对象:多个属性的动态集合,所谓动态就是可以新增属性也可以删除属性。
创建对象
- 直接量
- new Object()
- Object.create()
说到对象就必须说到原型 一般来说每个数据都是有原型的,并且可以通过对象.prototype
var o1 = Object.create(null); //不会继承任何东西,不能和“+”运算符一起正常工作
var o2 = Object.create(Object.prototype)
var o3 = new Object()
var o4 = {}
// o2,o3,o4 创建的结果是一样的
复制代码
属性特性
属性: 包括名字和值,属性名可以使包含空字符串在内的任意字符串,值可以是任意的js值。
每个属性都有自己的一些属性特性:
- 可写;
- 可枚举;
- 可配置;
可以通过Object.defineProperty()来设置这些属性特性的值
var p = {
//x和y是普通的可读写的数据属性
x: 1.0,
y: 1.o,
//r是可读写的存取器属性,它有getter和setter
get r(){
return Math.sqrt(this.X * this.x + this.y * this.y)
},
set r(newValue){
var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y)
var ratio = newvalue/oldvalue
this.x *=ratio;
this.y *=ratio;
},
//theta是只读存取器属性,它只有getter方法
get theta(){
return Math.atan2(this.y,this.x)
}
}
复制代码
那么我们下面来详细聊聊属性特性:
- 数据属性
我们可以认为一个属性包含一个名字和4个特性,数据属性的4个特性分别是它的值(value),可写性(writable),可枚举性(enumerable)和可配置性。
var o = {};
//添加一个不可枚举的数据属性x,并赋值为1
Object.defineProperty(o,'x',{
value: 1,
writable: true,
enumerable: false,
configurable: true
})
//属性存在,但是不可枚举
o.x; // => 1
Object.keys(o) // => []
//需要特别补充的是不可以通过defineProperty来修改继承属性
复制代码
- 存取器属性
但是对于存取器属性不具有值(value)特性和可写性,他们的可写性是由setter方法存在与否来决定的,因此对于存储器属性的4个特性是读取(get),写入(set),可枚举和可配置
var o = {
a: 1
};
//添加一个不可枚举的数据属性x,并赋值为1
Object.defineProperty(o,'x',{
enumerable: false,
configurable: true,
get(){
return this.a
},
set(val){
this.a = val
}
})
//属性存在,但是不可枚举
o.x; // => 1
Object.keys(o) // => ['a']
//需要特别补充的是
//1.不可以通过defineProperty来修改继承属性
//2. 如果需要同时创建或者修改多个属性时,可以使用Object.defineProperties()
复制代码
现在我们看到可以通过Object.defineProperty来修改属性属性和存取器属性,但是对于那些不允许创建或修改的属性来说,如果用Object.defineProperty()或者Object.defineProperties()则会抛出异常,比如给一个不可扩展的对象新增属性就会抛出类型错误异常
属性的查询和设置
可以通过.和方括号[]运算符来获取属性的值,假设要查询对象o的属性x,如果o中不存在x,那么将会继续在o的原型对象中查询属性x,---->一直往上找直到找到为止或者到null也找不到,这种一直往上找属性就构成了一个“链” 赋值操作需要注意的就是三点:假设给对象o的属性x赋值
- 如果o中继承自属性x,那么这个这个继承的属性就被新创建的同名属性覆盖
- 如果o继承自一个只读属性x,那么赋值操作是不允许的。也就是说这时候是无法同名覆盖的
- 如果o中没有属性x,并且o是不可扩展的,那么赋值会失败
删除属性
- delete 只能删除自有属性,不能删除继承属性
- delete 不能删除那些配置性为false的属性
- 通过var let等声明的 不能删除
检测属性
- in //可枚举的都可
- hasOwnPreperty() //对象的自有属性
- propertyIsEumerable() //自有属性且是可枚举的
枚举属性
- for in //所有可枚举的属性 包括继承的
- Object.keys() //所有可枚举的自身属性组成的数组
- Object.getOwnPropertyNames() //所有自有属性的数组,包括不可枚举的
对象特性
原型属性
这个属性来自原型, 怎么查询来自哪个原型:
- 在es3中:
o.constructor.prototype
复制代码
- 在es5中
Object.getPrototypeOf()
复制代码
类属性
es3和es5中都未提供设置设个属性的方法,并且只有一种间接的方法可以查询它,默认的toString()方法,
function classof(o){
if(o === null){
return 'Null';
}
if(o === undefined) return 'Undefined';
return Object.prototype.toString.call(o).slice(8,-1)
}
classof([]) // => Array
classof(1) // => Number
classof(function(){}) // => Function
复制代码
可扩展性
es5定义了用来查询和设置对象可扩展性的函数。通过将对象传入Object.esExtensible()来判断该对象是否可扩展,如果想转换为不可扩展 Object.preventExtensions() 需要注意的是这个操作是不可逆的,也就是说一旦将对象转换为不可扩展的,就无法再将其转换会可扩展的了。 另外要了解一下Object.seal()和Object.freeze(), vue文档中可是有说到Object.freeze()哦,不了解的同学赶紧查查吧!
// Object.seal()
//Object.isSealed()
// Object.freeze()
// Object.isFrozen()
复制代码
对象的深浅拷贝
- JSON.stringfy() JSON.parse()
- 递归拷贝
函数
函数声明式语句被提前,可以被在它定义之前出现的代码调用,但是以表达式定义的函数不能提前调用,变量的声明提前了,但是给变量赋值并不会提前。
函数调用
- 作为函数
- 作为方法
- 作为构造函数
- 通过它们的call()和apply()方法间接调用
任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参,call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数。
这里要说明一点对于很多初学者会很迷惑下面这两种写法,这里解释一下,如果构造函数没有形参,js构造函数调用的语法是允许省略圆括号的。
//下面两种写法在没有参数的时候是一样的
var o1 = new Object();
var o2 = new Object;
复制代码
实参对象 arguments
一个对象,碰巧具有以数字为索引的属性 =》 也称为类数组
闭包
函数内部访问函数外部的变量
函数定义时的作用域链在函数执行时依然有效。
常见经典考题:
- 请写一个方法,例如fn()不可以传参也不可以定义全局变量,每调用一次 输出值加1
function fn() {
let i = 0;
return function() {
console.log(i++)
}
}
let fn1 = fn();
fn1();
fn1()
//或者像书上这样写
var fn1 = (function(){
var counter = 0;
return function() {
return counter ++;
};
})()
fn1();
fn1()
复制代码
- 在来看看另一个经典考题:
var scope = "global scope"
function checkscope() {
var scope = "local scope";
function f() {
return scope
}
return f;
}
checkscope() // 返回值是什么 答案是local scope 而不是global scope
复制代码
回想一下词法作用域的基本规则: js函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的。嵌套的函数f()定义在这个作用域链里,其中的scope一定是局部变量,不管在任何时候执行函数f(),这种绑定在执行f()时依然有效。(死死咬住不放的感觉)
bind
将一个对象添加方法
//模拟bind实现
function bind(f,o){
if(f.bind) return f.bind(o);
else return function(){
return f.apply(o,arguments)
}
}
复制代码
另外 bind常用在柯里化
var sum = function(x,y){
return x+y
}
var succ = sum.bind(null,1);
succ(2) // => 3 x绑定到1,并传入2作为实参y
function f(y,z) {
return this.x + y +z
}
var g = f.bind({x:1},2)
g(3) //6
复制代码
以上就是自己总结的一些对象和函数常问的问题,部分给出了答案!当然有一些不足和不完善之处,欢迎各位指出!