js常见基础对象属性方法 (二)
关于es6的箭头函数的返回对象的问题
箭头函数(=>)具有隐式返回的特性。如果某个函数体只有单个表达式,你就可以忽略return关键字:()=>foo是一个不需要参数,而且最后会返回字符串foo
的函数。
而且需要注意的是,当你想要返回一个对象字面量的时候,如果你使用了大括号,javascript会默认你想要创建一个函数体。像是{ broken:true}。如果你想要通过隐式返回来返回一个字面量对象,那你就需要在你的字面量对象外面包裹一层小括号来消除这种歧义。
const noop = () => { foo: 'bar'}l
console.log(noop());// undefined
const createFoo = () => ({foo: 'bar'});
console.log(createFoo());// { foo: "bar"}
在第一个例子中,foo会被理解成一个标签,而bar会被理解成一个没有被赋值的表达式,这个函数会返回undefined.
而在createFoo()的例子中,圆括号强制让大括号里的内容被解释成为一个需要被计算的表达式,而不是一个函数体。
关于es6的解构在箭头函数中的的使用
先看一个函数的声明。
const createUser = ({ userName, avatar}) => {}
在这一行中,大括号({})代表了对象的解构。这个函数接受一个参数(一个对象)。但是从这个单一对象中又解构出了两个形参,userName和avatar。这些参数都可以被当作函数体作用域内的变量使用。你同样也可以解构一些数组:
const swap = ([first, second]) => [second, first];
console.log( swap([1,2]) ); // [2,1]
你也可以使用拓展运算符(...varName)来获取数组(或者参数列表)中的其他值,然后将这些数组元素回传成单个元素:
const rotate = ([first, ...rest]) => [...rest, first];
console.log( rotate([1,2,3])); // [2,3,1]
中括号在动态生成属性值的作用
先看一个示例
const arrToObj = ([key, value]) => ({ [key]: value });
console.log( arrToObj([ 'foo', 'bar' ]) ); // { "foo": "bar" }
在这个例子里,arrToObj将一个包含键值对(也叫元组)的数组转换成了一个对象。因为我们不知道键的名称,所以我们需要通过计算属性名来在对象中设置键值对。
这里需要知道js中访问对象属性的两者方式js对象属性中.号和中括号的区别。我们可以通过[]中括号去动态的设置对象属性属性名。
js对象属性中.号和中括号的区别。
js中访问对象属性有两者方式,一个是通过点号,一个是中括号。
1、中括号的运算符可以用字符串变量的内容作为属性名。点运算符不能。比如obj['string'+variable];即前者属性名可以是动态的。而后者需要是静态的
2、中括号运算符可以用纯数字作为属性名。点运算符不能。
3、中括号运算符可以用js的关键字和保留字作为属性名。点运算符不能。
//example
function aa(key,value){
console.log({[key]:value})
}
console.log(aa("asd","123"));// {"asd","123"}
js中判断空对象的方法
1、将json对象转化为json字符串,再判断该字符串是否为"{}"
var data = {};
var b = (JSON.stringify(data) === "{}")
console.log(b) //true
2、for in 循环判断
var obj = {};
var b = function(){
for( var key in obj) {
return false;
}
return true;
}
console.log(b);// true
3、Object.getOwnPropertyNames()方法
此方法是使用Object对象的getOwnPropertyNames方法,获取到对象中的属性名,存到一个数组中,通过判断数组的length来判断对象是否为空。
var data = {};
var arr = Object.getOwnPropertyNames(data);
alert(arr.length == 0);//true
4、使用ES6的Object.keys()方法
var data = {};
var arr = Object.keys(data);
console.log(arr.length === 0);//true
js中通过isNaN判断值是否为数字
isNaN() 函数用来确定一个值是否为NaN。
当算术运算返回一个未定义的或无法表示的值时,NaN就产生了。但是,NaN并不一定用于表示某些值超出表示范围的情况。将某些不能强制转换为数值的非数值转换为数值的时候,也会得到NaN。
可以通过isNaN去判断一个值是否为数字
if(isNaN(str)){
console.log("不是数字")
}else{
console.log("是数字")
}
// isNaN的polyfill
var isNaN = function(value){
var n = parseInt(value)
return n !== n
}
扩展:es6扩展了一个Number.isNaN的方法,传递的值是否为 NaN和其类型是 Number。它是原始的全局isNaN()的更强大的版本。
Number.isNaN
isNaN的扩展。和全局函数 isNaN()相比,该方法不会强制将参数转换成数字,只有在参数是真正的数字类型,且值为 NaN 的时候才会返回 true。
Number.isNaN(NaN); // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0) // true
// 下面这几个如果使用全局的 isNaN() 时,会返回 true。
Number.isNaN("NaN"); // false,字符串 "NaN" 不会被隐式转换成数字 NaN。
Number.isNaN(undefined); // false
Number.isNaN({}); // false
Number.isNaN("blabla"); // false
// 下面的都返回 false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");
// polyfill
Number.isNaN = Number.isNaN || function(value) {
return typeof value === "number" && isNaN(value);
}
JavaScript localeCompare() 方法
- 当 引用字符串 在 比较字符串 前面时返回 -1
- 当 引用字符串 在 比较字符串 后面时返回 1
- 相同位置时返回 0
localeCompare()方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。
返回一个数字表示是否 引用字符串 在排序中位于 比较字符串 的前面,后面,或者二者相同。
//可以用该方法结合sort方法对字符串数组进行排序:
var str="abbbbAAbcBCCccdaACBDDabcccddddaab";
str.join().sort(function(a,b){return a.localeCompare(b)})
Object.freeze()
Object.freeze()方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改已有的属性的值,不能删除已有属性,以及不能修改对象已有的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
//examples
const object1 = {
property1:42
};
const object2 = Object.freeze(object1);
object2.property1 = 33;
// Throws an error in strict mode
console.log(object2.property1);
// expected output:42
注意:
1、被冻结的对象自身的所有属性都不可能以任何方式被修改(浅冻结情况下,如果被冻结的对象含有对象属性,则该对象属性不会被冻结)。任何修改尝试都会失败,一般会静默或者抛出TypeError异常
2、数据属性的值不可更改,访问器属性(有getter和setter)也同样。如果一个属性的值是个对象,在这个对象中的属性是可以修改的,除非它也是个冻结对象。
3、这个方法返回传递的对象,而不是创建一个被冻结的副本。所以不需要将返回的结果重新赋给一个新的对象,因为指向的都是同一个对象。
Object.defineProperty()
defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有的属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
obj 要在其上定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。
返回:被传递给函数的对象。
Array.prototype.reduce()
reduce(callback[, initialValue])方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
const array1 = [1,2,3,4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output:10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output:15
参数:
callback:执行数组中每个值的函数,包含四个参数:
1、accumulator
累加器累加回调的返回值;它是上一次调用回调时返回的累积值,或initialValue
2、currentValue
数组中正则处理的元素
3、currentIndex(可选)
数组中正在处理的当前元素的索引。如果提供了initialValue,则索引号为0,否则索引为1。
4、array(可选)
调用reduce的数组
initialValue(可选)
用作第一个调用callback的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用reduce将报错。
返回值:
函数累计处理的结果。
Array.prototype.concat()
concat()方法用于合并两个或多个数组,此方法不会更改现有数组,而是返回一个新数组。
with 语句
with语句 扩展一个语句的作用域链。
with (expression) {
statement
}
expression 将给定的表达式添加到在评估语句时作用的作用域链上。表达式周围的括号是必需的。
statement
任何语句。要执行多个语句,请使用一个块语句对这些语句进行分组
描述:javascript 查找某个未使用变量时,会通过作用域链来查找,作用域链是跟执行代码的context或者包含这个变量的函数有关。'with'语句将某个对象添加的作用域链的顶部,如果在statement中又某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果没有同名的属性,则将抛出RefrenceError异常
另外:with 在严格模式下被禁用,替代方式是声明一个临时变量来承载你所需要的属性。
简单来说,with 可以减少变量的长度。比如this。使用with不需要在一个函数块中大量的使用this+"."的方式去访问对象属性,可以直接使用属性名就可以访问到属性的值。
Object.assign()
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
描述:如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
Function.prototype.apply()
apply(thisArg, [argsArray]) 方法调用一个函数,其具有一个指定的this值,以及作为一个数组提供的参数。
注意:call()方法的作用和apply()方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
参数:
thisArg。可选参数
在func函数运行时使用的this值。需要注意的是,使用的this值不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则制定为null或undefined时会自动替换为指向全局对象(浏览器中就是window对象),同时值为原始值(数字、字符串、布尔值)的this会指向该原始值的包装对象。
argsArray 可选参数
一个数组或者类数组对象,其中的数组元素作为单独的参数传给func函数。如果该参数的值为null或undefined。
返回值:
调用有指定this值和参数的函数的结果。
示例:使用apply来链接构造器(函数的constructor属性指向函数本身)
// examples
Function.prototype.construct = function (aArgs) {
var oNew = Object.create(this.prototype);// 定义一个对象,该对象的原型指向函数的原型属性(prototype)
this.apply(oNew,aArgs); //使用该对象继承函数的对象属性,这样就可以实现constructor指向函数本身
return oNew;
}
function MyConstructor () {
for (var nProp = 0; nProp < arguments.length; nProp++) {
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1); // logs "Hello world!"
console.log(myInstance instanceof MyConstructor); // logs "true"
console.log(myInstance.constructor); // logs "MyConstructor"
EventTarget.addEventListener()
- 语法:
target.addEventListener(type, listener, options);
参数:
- type:表示监听事件类型的字符串.
- listener:当所监听的事件类型触发时,会接收到一个事件通知(实现了Event接口的对象)对象。listener必须是一个实现了EventListener接口的对象,或者是一个函数。
- options:一个指定有关listener属性的可选参数对象。可用的选项如下:
capture:Boolean,表示listener会在该类型的事件捕获阶段传播到该EventTarget时触发
once:Boolean,表示listener在添加之后最多只调用一次。如果是true,listenter会在其被调用之后自动移除。
passive:Boolean。表示listener永远不会调用preventDefault().如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。关于该属性具体介绍,可移步下个关于passive属性的介绍。
mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。
- useCapture
Boolean,是指在DOM树中,注册了该listener的元素,是否会先于它下方的任何事件目标,接收到该事件。沿着DOM树向上冒泡的事件不会触发被指定为usecapture的listener。当一个元素嵌套了另一个元素,两个元素都对同一个事件注册一个处理函数是,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以那个顺序接收事件。
注意:那些不支持参数options的浏览器,会把第三个参数默认为useCapture,即设置useCapture为true
addEventListener的passive属性
document.addEventListener("touchstart", function(e){
... // 浏览器不知道这里会不会有 e.preventDefault()
},passive:true)
由于浏览器不知道当我们在移动端监听touch事件,如touchstart时。是否有做 e.preventDefault,即阻止默认行为。所以浏览器必须要执行完整个监听函数才知道是否有阻止默认行为的代码(比如页面滚动),从而让页面进行滚动。这无疑会对浏览器的滚动性能造成卡顿。
而passive属性,就是为了告诉浏览器,我的监听事件里面没有调用e.preventDefault,这样浏览器就可以不用管监听事件里面的内容,而直接滚动。当然如果监听器里面依然调用了e.preventDefault,那么客户端也会忽略他,并且抛出一个警告。
对于浏览器是否支持passive属性检测实例。
var passiveSupported = false;
try {
var options = Object.defineProperty({},'passive',{
get: function() {
passiveSubpported = true;
}
})
window.addEventLisener("test",null,options);
} catch(err){}
这段代码为passive属性创建一个带有getter函数的options对象;getter设定了一个标识,passiveSupported,被调用后就会把其设为true。那意味着如果浏览器检查options对象上的passive指时,passiveSupported就会被设置为true;否则它将保持false.然后我们调用addEventListener()去设置一个指定这些选项的空事件处理器,这样如果浏览器将第三个参数认定为对象的话,这些选项指就会被检查。
然后,当你想实际创建一个是否支持options的事件侦听器时,你可以这样做:
someElement.addEventListener("mouseup",handleMouseUp,passiveSupported ? {passive:true} : false)
js事件中target和currentTarget的区别与联系
1、target:触发事件的某个对象,一般出现的事件流的目标阶段。
2、currentTarget:绑定事件的对象,可能会出现在事件流的任意一个阶段中。
3、通常情况下target和currentTarget是一致的。我们只要使用target即可,但有一种情况下,必须要区分这二者之间的关系,那就是在父子嵌套的关系中,父元素绑定了事件,点击子元素(根据事件流,在不阻止事件流的前提下他会传递至父元素,导致父元素的事件处理函数执行)。这种情况下,currentTarget指向的是父元素,因为他是绑定事件的对象,而target指向了子元素,因为他是触发事件的那个具体对象。
示例:
<div id="one">
<div id="three"></div>
</div>
one.addEventlistener('click',function(e){
console.log(e.target); //three
console.log(e.currentTarget)
})
事件流的事件捕获以及事件冒泡的执行顺序
1、事件捕获:事件从window顶层向事件触发的元素传播的过程。
2、事件冒泡:事件从触发事件的元素向window顶层传播的过程。
3、ie最开始提出的事件冒泡,而w3c提出的是事件捕获,所以现在才会有这两种事件的传播方式。
4、现代浏览器的一般解析这两种事件流的顺序:事件捕获--》目标阶段-》事件冒泡。
通过一个示例看下两个不同的事件流的顺序关系。
<div id="one">
<div id="two">
<div id="three"></div>
</div>
</div>
one.addEventLister('click',function(e){
console.log('one');
},false)
two.addEventLister('click',function(e) {
console.log('two');
},false)
three.addEventListener('click',function(e){
console.log('three');
},true);
//three
//two
//one
one.addEventLister('click',function(e){
console.log('one');
},true)
two.addEventLister('click',function(e) {
console.log('two');
},true)
three.addEventListener('click',function(e){
console.log('three');
},true);
//当三个均为捕获时。结果正好相反
//one
//two
//three
//一个是自上而下触发事件,一个是自下而上触发事件。所以导致了两种绑定方式结果的不同
addEventListener方法可以允许传第二个位置一个参数,告诉浏览器这里你添加的事件是在事件捕获阶段执行,还是在事件冒泡阶段执行。默认参数为false,为冒泡。为true,为捕获