javascript怎么实现如下所述python实现二维数组组去重

第一种是比较常规的方法
1.构建一个新的数组存放结果
2.for循环中每次从原数组中取出一个元素,用这个元素循环与结果数组对比
3.若结果数组中没有该元素,则存到结果数组中
Array.prototype.unique1 = function(){
var res = [this[0]];
for(var i = 1; i & this. i++){
var repeat = false;
for(var j = 0; j & res. j++){
if(this[i] == res[j]){
repeat = true;
if(!repeat){
res.push(this[i]);
var arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0]
alert(arr.unique1());
第二种方法比上面的方法效率要高
1.先将原数组进行排序
2.检查原数组中的第i个元素 与 结果数组中的最后一个元素是否相同,因为已经排序,所以重复元素会在相邻位置
3.如果不相同,则将该元素存入结果数组中
Array.prototype.unique2 = function(){
this.sort(); //先排序
var res = [this[0]];
for(var i = 1; i & this. i++){
if(this[i] !== res[res.length - 1]){
res.push(this[i]);
var arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0]
alert(arr.unique2());
第二种方法也会有一定的局限性,因为在去重前进行了排序,所以最后返回的去重结果也是排序后的。如果要求不改变数组的顺序去重,那这种方法便不可取了。
第三种方法(推荐使用)
1.创建一个新的数组存放结果
2.创建一个空对象
3.for循环时,每次取出一个元素与对象进行对比,如果这个元素不重复,则把它存放到结果数组中,同时把这个元素的内容作为对象的一个属性,并赋值为1,存入到第2步建立的对象中。
说明:至于如何对比,就是每次从原数组中取出一个元素,然后到对象中去访问这个属性,如果能访问到值,则说明重复。
Array.prototype.unique3 = function(){
var res = [];
var json = {};
for(var i = 0; i & this. i++){
if(!json[this[i]]){
res.push(this[i]);
json[this[i]] = 1;
var arr = [112,112,34,'你好',112,112,34,'你好','str','str1'];
alert(arr.unique3());
阅读(...) 评论()js数组去重,老生长谈,今天对其进行一番归纳,总结出来4种方法
贴入代码前 ,先对浏览器Array对象进行支持indexOf和forEach的polyfill
Array.prototype.indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, j = this. i & i++) {
if (this[i] === item) {
return -1;
Array.prototype.forEach = Array.prototype.forEach || function(callback, thisArg) {
if (!callback || typeof callback !== 'function') return;
for (var i = 0, j = this. i & i++) {
callback.call(thisArg, this[i], i, this);
方法一:遍历数组,建立新数组,利用indexOf判断是否存在于新数组中,不存在则push到新数组,最后返回新数组
1 function removeDuplicatedItem(ar) {
var ret = [];
for (var i = 0, j = ar. i & i++) {
if (ret.indexOf(ar[i]) === -1) {
ret.push(ar[i]);
方法二:遍历数组,利用object对象保存数组值,判断数组值是否已经保存在object中,未保存则push到新数组并用object[arrayItem]=1的方式记录保存
function removeDuplicatedItem2(ar) {
var tmp = {},
for (var i = 0, j = ar. i & i++) {
if (!tmp[ar[i]]) {
tmp[ar[i]] = 1;
ret.push(ar[i]);
方法三:数组下标判断法, 遍历数组,利用indexOf判断元素的值是否与当前索引相等,如相等则加入
function removeDuplicatedItem3(ar) {
var ret = [];
ar.forEach(function(e, i, ar) {
if (ar.indexOf(e) === i) {
ret.push(e);
方法四:数组先排序, 然后比较俩数组一头一尾进行去重
function removeDuplicatedItem4(ar) {
var ret = [],
ar.sort();
end = ar[0];
ret.push(ar[0]);
for (var i = 1; i & ar. i++) {
if (ar[i] != end) {
ret.push(ar[i]);
end = ar[i];
&有其他好的方式 ,欢迎补充。
阅读(...) 评论()如何用JavaScript进行数组去重7 months ago0收藏分享举报文章被以下专栏收录观点·洞察·新知{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&isPending&:false,&contributes&:[{&sourceColumn&:{&lastUpdated&:,&description&:&&,&permission&:&COLUMN_PUBLIC&,&memberId&:,&contributePermission&:&COLUMN_PUBLIC&,&translatedCommentPermission&:&all&,&canManage&:true,&intro&:&观点·洞察·新知&,&urlToken&:&getui&,&id&:22030,&imagePath&:&v2-ce535ef9cfde8f21ece0a.jpg&,&slug&:&getui&,&applyReason&:&0&,&name&:&个推&,&title&:&个推&,&url&:&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fgetui&,&commentPermission&:&COLUMN_ALL_CAN_COMMENT&,&canPost&:true,&created&:,&state&:&COLUMN_NORMAL&,&followers&:101,&avatar&:{&id&:&v2-ce535ef9cfde8f21ece0a&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&},&activateAuthorRequested&:false,&following&:false,&imageUrl&:&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-ce535ef9cfde8f21ece0a_l.jpg&,&articlesCount&:47},&state&:&accepted&,&targetPost&:{&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-2f2b8a832bdf3dc852392c_r.jpg&,&lastUpdated&:,&imagePath&:&v2-2f2b8a832bdf3dc852392c.jpg&,&permission&:&ARTICLE_PUBLIC&,&topics&:[295],&summary&:&\u003Ci\u003E作者:个推WEB前端首席架构师姜季廷\u003C\u002Fi\u003E今天的文章和大家谈一谈如何用JavaScript进行数组去重,这是一道常见的面试(笔试)题,可以很好地考察出一个人的逻辑思维及边界考虑情况,希望此文能够帮助大家在解决类似问题时拓宽思路。据我到目前为止面试的情况,很…&,&copyPermission&:&ARTICLE_COPYABLE&,&translatedCommentPermission&:&all&,&likes&:0,&origAuthorId&:0,&publishedTime&:&T14:38:30+08:00&,&sourceUrl&:&&,&urlToken&:,&id&:3531811,&withContent&:false,&slug&:,&bigTitleImage&:false,&title&:&如何用JavaScript进行数组去重&,&url&:&\u002Fp\u002F&,&commentPermission&:&ARTICLE_ALL_CAN_COMMENT&,&snapshotUrl&:&&,&created&:,&comments&:0,&columnId&:22030,&content&:&&,&parentId&:0,&state&:&ARTICLE_PUBLISHED&,&imageUrl&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-2f2b8a832bdf3dc852392c_r.jpg&,&author&:{&bio&:&推送\u002F大数据&,&isFollowing&:false,&hash&:&0032205cbb4bbaabbab233&,&uid&:583500,&isOrg&:false,&slug&:&hong-jack-49&,&isFollowed&:false,&description&:&&,&name&:&Hong Jack&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fhong-jack-49&,&avatar&:{&id&:&cf1fc371b328d477d726&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&memberId&:,&excerptTitle&:&&,&voteType&:&ARTICLE_VOTE_CLEAR&},&id&:755335}],&title&:&如何用JavaScript进行数组去重&,&author&:&hong-jack-49&,&content&:&\u003Cp\u003E\u003Ci\u003E作者:个推WEB前端首席架构师姜季廷\u003C\u002Fi\u003E\u003C\u002Fp\u003E\u003Cp\u003E今天的文章和大家谈一谈如何用JavaScript进行数组去重,这是一道常见的面试(笔试)题,可以很好地考察出一个人的逻辑思维及边界考虑情况,希望此文能够帮助大家在解决类似问题时拓宽思路。据我到目前为止面试的情况,很少有人能在现场考虑很全,基本上的人都是浅尝辄止。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E当然,“使用库中的一个函数就能去重”并不在本篇文章的讨论范围内,我们针对的是需要自己写代码的场景。考虑到实际情况,我们使用ES5(主要就用了indexOf方法,如果是更古老的环境,可以自己增加这段代码,或者使用ES5兼容库es5-sham.js)。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E我们先审题:数组,题目中并没有说是什么样的数组,即数组的组成元素可能是字符串、数字、布尔、数组、对象、Null、Undefined。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E在开始之前我们先看看这些类型以及他们的值比较关系:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cnoscript\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_b.jpg\& data-rawwidth=\&476\& data-rawheight=\&529\& class=\&origin_image zh-lightbox-thumb\& width=\&476\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_r.jpg\&\u003E\u003C\u002Fnoscript\u003E\u003Cimg src=\&data:image\u002Fsvg+utf8,&svg%20xmlns='http:\u002F\u002Fwww.w3.org\u002FFsvg'%20width='476'%20height='529'&&\u002Fsvg&\& data-rawwidth=\&476\& data-rawheight=\&529\& class=\&origin_image zh-lightbox-thumb lazy\& width=\&476\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_r.jpg\& data-actualsrc=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_b.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E接着我们来看看数组中的indexOf方法:\u003C\u002Fp\u003E\u003Cp\u003Evar gtArray = [66],\u003C\u002Fp\u003E\u003Cp\u003EgtObject = {\u003C\u002Fp\u003E\u003Cp\u003Eid: 1\u003C\u002Fp\u003E\u003Cp\u003E},\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr = [\&1\&, 1, true, [66],\u003Cbr\u003EgtArray, { id: 1 }, gtObject, null, undefined];\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(\&1\&);\u003Cbr\u003E\u002F\u002F 0\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(1); \u002F\u002F 1\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(true); \u002F\u002F 2\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf([66]); \u002F\u002F -1\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(gtArray); \u002F\u002F 4\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf({ id: 1 }); \u002F\u002F\u003Cbr\u003E-1\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(gtObject); \u002F\u002F 6\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(null); \u002F\u002F 7\u003C\u002Fp\u003E\u003Cp\u003EgtTestArr.indexOf(undefined); \u002F\u002F\u003Cbr\u003E8\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E从上述效果中看我们可以得出结论:indexOf 可以帮我们找到一个数组中某个元素(若该元素为数组或者对象,则为该引用的地址值)对应的索引值,在人脑“看”来相同的[66] 和 gtArray,实际上除了都用gtArray表示的部分是一样的,其他的 [66]之间以及gtArray都是不同的引用地址,自然也就找不到索引值啦 。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E好了,回归正题,我们要进行数组去重,那么先想个大致的思路,比如:\u003C\u002Fp\u003E\u003Cp\u003E1)新建一个空数组,老数组从第一个开始,看看新数组中有没有,如果没有就push进入新数组,如果存在就下一个。\u003C\u002Fp\u003E\u003Cp\u003E2)在一个数组里面从第一个开始,将它后面的元素依次与当前这个比较,如果相等,就把后面的那个元素删掉,依次往复操作,直到最后一个。接着比较对象变成第二个,重复上述步骤,直到比较对象是最后一个。\u003C\u002Fp\u003E\u003Cp\u003E3)and so on\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E当然每个思路有不同的算法,对于一种判断描述也可以有不同的实现方式(如下面的相等),比如用 map,用下标等。不同方式可能也会有不同的局限性或者前置条件。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E好,现在我们界定一下什么是相等,简单的 1 与 1 肯定是相等的,而1 与 “1”是不等的,对于引用类型我们可以分为几种模式(级别):\u003C\u002Fp\u003E\u003Cp\u003E1)仅引用地址一样才算相等。\u003C\u002Fp\u003E\u003Cp\u003E2)引用地址可以不一样,但对应的数组(对象)所拥有的元素(键值对)一模一样就算相等。 即在我们看来,这两个数据写出来,看上去就是一样的。\u003C\u002Fp\u003E\u003Cp\u003E3)对于是非数组的对象,针对几个key的值是一样的情况,我们将其认定是一样的。比如 { id: 1, name: ”张三” } 和 { id: 1, name: ”李四” } 在只考虑 id 字段时他们就是一样的。当然这种类型是我们人为赋予的模式。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E好了,准备工作已做好,我们开始码代码吧。\u003C\u002Fp\u003E\u003Cp\u003E按照思路1,相等的模型取第二种,直接上代码如下:\u003C\u002Fp\u003E\u003Cp\u003Efunction gtUniqueArr(arr) {\u003C\u002Fp\u003E\u003Cp\u003Evar i,\u003C\u002Fp\u003E\u003Cp\u003EnewArr = [];\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003Efunction isExist(_item, _arr) {\u003C\u002Fp\u003E\u003Cp\u003Evar k,\u003C\u002Fp\u003E\u003Cp\u003Efind =\u003C\u002Fp\u003E\u003Cp\u003Eif (typeof _item !== \&object\&\u003Cbr\u003E|| _item === null) {\u003C\u002Fp\u003E\u003Cp\u003Ereturn _arr.indexOf(_item) & -1;\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003Efor (k in _arr) {\u003C\u002Fp\u003E\u003Cp\u003Eif (_arr.hasOwnProperty(k)) {\u003C\u002Fp\u003E\u003Cp\u003Efind = isEqual(_item, _arr[k]);\u003C\u002Fp\u003E\u003Cp\u003Eif (find) {\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E};\u003C\u002Fp\u003E\u003Cp\u003Efunction isEqual(_a, _b) {\u003C\u002Fp\u003E\u003Cp\u003Evar k,\u003C\u002Fp\u003E\u003Cp\u003EkeysA,\u003C\u002Fp\u003E\u003Cp\u003EkeysB,\u003C\u002Fp\u003E\u003Cp\u003Eequal =\u003C\u002Fp\u003E\u003Cp\u003Eif (typeof _a !== \&object\& ||\u003Cbr\u003E_a === null ||\u003C\u002Fp\u003E\u003Cp\u003Etypeof _b !== \&object\& ||\u003Cbr\u003E_b === null) { \u002F\u002F 有非引用类型(数组与对象)或者有NULL类型时直接判断\u003C\u002Fp\u003E\u003Cp\u003Ereturn _a === _b;\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u002F\u002F _a _b 不同为数组或者对象时 直接认为不同,否则长得像数组的对象也会互判相等\u003C\u002Fp\u003E\u003Cp\u003Eif (_a instanceof Array !== _b\u003Cbr\u003Einstanceof Array) {\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u002F\u002F 同为对象或者数组\u003C\u002Fp\u003E\u003Cp\u003EkeysA = Object.keys(_a);\u003C\u002Fp\u003E\u003Cp\u003EkeysB = Object.keys(_b);\u003C\u002Fp\u003E\u003Cp\u003Eif (keysA.length !== keysB.length) { \u002F\u002F\u003Cbr\u003E元素量不同肯定就不是一样了啊\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u002F\u002F 其实也可以先判断下引用地址是否一样,一样肯定就相等啦\u003C\u002Fp\u003E\u003Cp\u003Efor (k in _a) {\u003C\u002Fp\u003E\u003Cp\u003Eif (_a.hasOwnProperty(k)) {\u003C\u002Fp\u003E\u003Cp\u003Eequal = isEqual(_a[k], _b[k]);\u003C\u002Fp\u003E\u003Cp\u003Eif (!equal) {\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E};\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003Eif (arr && arr.length) {\u003C\u002Fp\u003E\u003Cp\u003Efor (i = 0; i & arr. i++) {\u003C\u002Fp\u003E\u003Cp\u003Eif (!isExist(arr[i], newArr)) {\u003C\u002Fp\u003E\u003Cp\u003EnewArr.push(arr[i]);\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003Ereturn newA\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E我们可以把isExist,isEqual提取成公共函数,按照思路2,相等类型依然为第二种,上代码:\u003C\u002Fp\u003E\u003Cp\u003Efunction gtUniqueArr(arr) {\u003C\u002Fp\u003E\u003Cp\u003Evar i,\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003Eif (arr && arr.length) {\u003C\u002Fp\u003E\u003Cp\u003Efor (i = 0; i & arr. i++) {\u003C\u002Fp\u003E\u003Cp\u003Efor (j = i + 1; j & arr.\u003Cbr\u003Ej++) {\u003C\u002Fp\u003E\u003Cp\u003Eif (isEqual(arr[i], arr[j])) {\u003C\u002Fp\u003E\u003Cp\u003Earr.splice(j, 1);\u003C\u002Fp\u003E\u003Cp\u003Ej--; \u002F\u002F 复原因数组删除导致的遗漏了的元素指向\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E\u003C\u002Fp\u003E\u003Cp\u003E}\u003C\u002Fp\u003E\u003Cp\u003E当然,要采取不同的相等模式,只要改变 isEqual 函数即可,此处其他两种相等模式(或者你还有其他假设的相等模式)诉求相对较少,此处便不再展开叙述了(模式1,直接用===比较两者即可;模式3,用===检测要求的字段的值是否一样)。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E当我们的环境是ES6时,一般的去重标准可以使用 set 来做:\u003C\u002Fp\u003E\u003Cp\u003Evar rs = new Set(arr);\u003C\u002Fp\u003E\u003Cp\u003E但是当数组元素为引用类型时,引用地址不一样但在我们看来是完全一样的两个元素,这个方法是去不掉的。\u003C\u002Fp\u003E&,&updated&:new Date(&T06:38:30.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:0,&collapsedCount&:0,&likeCount&:0,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-2f2b8a832bdf3dc852392c_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&reviewers&:[],&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Java&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&JavaScript&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&数组去重&}],&adminClosedComment&:false,&titleImageSize&:{&width&:800,&height&:444},&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&column&:{&slug&:&getui&,&name&:&个推&},&tipjarState&:&inactivated&,&annotationAction&:[],&sourceUrl&:&&,&pageCommentsCount&:0,&hasPublishingDraft&:false,&snapshotUrl&:&&,&publishedTime&:&T14:38:30+08:00&,&url&:&\u002Fp\u002F&,&lastestLikers&:[],&summary&:&\u003Cimg src=\&http:\u002F\u002Fpic4.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_200x112.png\& data-rawwidth=\&476\& data-rawheight=\&529\& class=\&origin_image inline-img zh-lightbox-thumb\& data-original=\&http:\u002F\u002Fpic4.zhimg.com\u002Fv2-b1e6d0d303f362bd9b806b_r.png\&\u003E\u003Ci\u003E作者:个推WEB前端首席架构师姜季廷\u003C\u002Fi\u003E今天的文章和大家谈一谈如何用JavaScript进行数组去重,这是一道常见的面试(笔试)题,可以很好地考察出一个人的逻辑思维及边界考虑情况,希望此文能够帮助大家在解决类似问题时拓宽思路。据我到目前为止面试的情况,很…&,&reviewingCommentsCount&:0,&meta&:{&previous&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Apple Push Notification Service&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&推送 (Push)&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&信息推送&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&新媒体运营&,&isFollowing&:false,&hash&:&9f2a03bd2cdc46037e6a&,&uid&:978100,&isOrg&:false,&slug&:&neroli-79&,&isFollowed&:false,&description&:&想把严肃的知识活泼地表达&,&name&:&Neroli&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fneroli-79&,&avatar&:{&id&:&v2-c040c89cd24b8a82059ff0&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&content&:&\u003Cp\u003E\u003Cb\u003E序言:\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E
因为App的功能需要,最近一直在调研苹果的APNs推送,开始时觉得超麻烦,现在感觉还是比较easy,“难者不会,会者不难”,自己踩过了这么多的坑终于会了,不出来吐槽(装X)一下对不起自己,23333。\u003C\u002Fp\u003E\u003Cp\u003E
先给大家来一个小小福利,因为APNs只能在真机上测试,模拟器上用不了的,如果还没有iPhone的同学,可以借这个机会找老板申请一台iPhone,能不能申请下来就看本事了,我就是没有iPhone,然后用这种方式让公司帮配的iPhone。\u003C\u002Fp\u003E\u003Cp\u003E
然后就是需要写服务代码,在测试发送APNs消息的时候,需要写服务器代码给苹果服务器发消息,本人作为一个纯iOS开发者,对服务器代码十窍通九窍,还好现在网上很多第三方提供这个功能,因为我们公司使用的是个推,就直接使用个推提供的功能测试了,不需要我来写服务端代码真爽。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E准备工作:\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E
1、苹果开发许可证书,分为:开发证书 (iOS App Development)、生产证书(App Store and Ad Hoc)等,后面我使用的是开发证书进行测试。\u003C\u002Fp\u003E\u003Cp\u003E
2、苹果开发者网站上注册“AppIDs”,我使用的是“com.crazywolf.yewan”,\n勾选“Push Notifications”。\u003C\u002Fp\u003E\u003Cp\u003E
3、真机(加油,iPhone在等着你),需要添加到开发许可设备中。\u003C\u002Fp\u003E\u003Cp\u003E
4、Provisioning\nProfiles文件,分:开发时使用(iOS App Development)、生产时使用(App Store、Ad Hoc)等,我在后面使用的是“Development”。\u003C\u002Fp\u003E\u003Cp\u003E
5、苹果APNs推送证书,分:开发环境证书 (Development)、生产环境证书(Production)等,同样,也是使用“Development”,注意使用个推平台APNs推送需上传该推送证书,这里我将导出的开发环境证书提交个推平台,关于证书生产和导出可以查看个推 APNs配置技术文档(\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fdocs.getui.com\u002Fmobile\u002Fios\u002Fapns\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E苹果APNs配置 -\u003C\u002Fa\u003E)。\u003C\u002Fp\u003E\u003Cp\u003E
6、Xcode8.2(不同版本在配置时有点不同),最低支持版本iOS 8.0。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E\u003Cb\u003E一:注册APNs、获取DeviceToken\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E1、创建新项目或修改老项目,配置项目\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-aede3c3ba663cb7d23148b0_b.jpg\& data-rawwidth=\&618\& data-rawheight=\&413\& class=\&origin_image zh-lightbox-thumb\& width=\&618\& data-original=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-aede3c3ba663cb7d23148b0_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E2、注册APNs,获取DeviceToken\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-6a139f887bbec6e3db28b704bef35818_b.jpg\& data-rawwidth=\&626\& data-rawheight=\&426\& class=\&origin_image zh-lightbox-thumb\& width=\&626\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-6a139f887bbec6e3db28b704bef35818_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E3、使用个推的测试一下,测试DeviceToken\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-6db94cdc7a8a0a069aa977_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&379\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-6db94cdc7a8a0a069aa977_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-a69f38df20c847c535bf194b40eabffc_b.jpg\& data-rawwidth=\&340\& data-rawheight=\&205\& class=\&content_image\& width=\&340\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E是不是很简单,这样就可以获取到APNs推送消息了,有没有一种成功感,不过我开始获取DeviceToken时,一直报(“Error Domain=NSCocoaErrorDomain Code=3000\n\&未找到应用程序的“aps-environment”的授权字符串\& UserInfo={NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}”)错误,网上说是证书没配置好,我重新配置了多次证书还是不行,后来问了个推的技术支持才知道Xcode8以上版本需要打开“TARGETS -\nCapabilities - Push Notifications”,个推的集成文档中也有写,自己太粗心了。\u003C\u002Fp\u003E\u003Cp\u003E 4、APNs环境问题\u003C\u002Fp\u003E\u003Cp\u003E
注意保持推送APNs环境和你的App推送环境一致,因为经常有人会把证书环境搞错,导致推送收不到。\u003C\u002Fp\u003E\u003Cp\u003E
1)直接使用Xcode直接运行到手机上,可以根据“TARGETS -& General\n-& Signing”中“Provisioning Profile”和“Signing Certificate”来确认。例如下图:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-8b2bee30fbcf3522e95b_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&259\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-8b2bee30fbcf3522e95b_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-bd3fed0c77ac7961b61ccff4082be78d_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&254\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-bd3fed0c77ac7961b61ccff4082be78d_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E分享一下,我在给“Provisioning Profile”文件命名时有个习惯,以“Dev: ”(开发环境)、“In House: ”(企业包环境)、“XC Ad Hoc: ”(分发包环境)、“XC: ”(App Store),其中后面三个都是生产环境。\u003C\u002Fp\u003E\u003Cp\u003E
2)打包成 ipa 包安装到iPhone上,可能会忘记打包时的配置或者是其他人发你的包,是不是就不能知道APNs的环境了? 很早之前我的方法是获取App的\nDeviceToken,使用开发和生产环境APNs证书都推送一下,看看是哪个能推送到。后来发现了还有其他方法的,那就是解析ipa包:\u003C\u002Fp\u003E\u003Cp\u003E
1)先解压ipa包,找到.app文件,显示包内容\u003C\u002Fp\u003E\u003Cp\u003E
2)找到.mobileprovision文件,使用“Atom”打开.mobileprovision文件\u003C\u002Fp\u003E\u003Cp\u003E
3)查找“aps-environment”,查看“aps-environment”这个key值对应value,“development”表示开发环境,“production”表示生产环境。如下图:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-ce500d62be5_b.jpg\& data-rawwidth=\&302\& data-rawheight=\&88\& class=\&content_image\& width=\&302\&\u003E\u003C\u002Ffigure\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-1f93eea2f8a6bea41acac1_b.jpg\& data-rawwidth=\&302\& data-rawheight=\&87\& class=\&content_image\& width=\&302\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E\u003Cb\u003E二:正式推送APNs,推送我们需要的信息\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E1、集成个推SDK\u003C\u002Fp\u003E\u003Cp\u003E
怎么配置个推,可以去看“\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fdocs.getui.com\u002Fmobile\u002Fios\u002Fxcode\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EXcode集成 -\u003C\u002Fa\u003E\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fdocs.getui.com\u002Fmobile\u002Fios\u002Fxcode\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E\u002F\u003C\u002Fa\u003E”,配置成功后运行获取个推的“clientId”。\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-cbcd6ae9dc1dbccc05bfd_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&124\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-cbcd6ae9dc1dbccc05bfd_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E2、使用个推网站上的“透传消息”下发\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-53ecb03ee260d24cbcddc97_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&402\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-53ecb03ee260d24cbcddc97_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-0b6297bdce105cb5b7ef93b4bf462548_b.jpg\& data-rawwidth=\&340\& data-rawheight=\&280\& class=\&content_image\& width=\&340\&\u003E\u003C\u002Ffigure\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-f06c64d001bed_b.jpg\& data-rawwidth=\&340\& data-rawheight=\&110\& class=\&content_image\& width=\&340\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E这样就可以推送自定义消息内容到iPhone上了,到这里APNs的功能已经全部完成了,后面就要看看具体需求了,将个推的服务端集成部分发给你们服务端开发人员,让他“码”起来了,如果有问题,让他们联系个推的技术支持,2333。\u003C\u002Fp\u003E\u003Cp\u003E3、APNs消息统计\u003C\u002Fp\u003E\u003Cp\u003E
个推最新版本1.5.3 iOS SDK添加了“iOS 10 APNs展示统计”功能,该功能使用到了iOS10 新特性需要添加NotificationService 扩展模块,能准确统计到 iOS10 以上 APNs 展示信息,这个功能太爽了,APNs\n展示数据无法统计是多少开发者及运营的痛啊,相信有了这个功能能更好的跟踪APNs推送到达情况。具体集成步骤可以查看“\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fdocs.getui.com\u002Fmobile\u002Fios\u002Fxcode\u002F%236-ios-10-apns\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EXcode集成 -\u003C\u002Fa\u003E”。\u003C\u002Fp\u003E\u003Cp\u003E
推送成功后可以在个推后台进行查看推送情况,\n如图:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-e2f719a2dacf_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&279\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-e2f719a2dacf_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E个推渠道下发还是区分蛮清晰的, 个推成功下发为通过个推通道进行下发, APNs成功下发模块为离线后,走APNs通道下发, 其中上面说的展示统计数据就是 APNs模块中的展示数了。\n用户量有点小,别介意哈 zZZ。 \u003C\u002Fp\u003E\u003Cp\u003E4、回调方法区别\u003C\u002Fp\u003E\u003Cp\u003E
APNs的消息在App不同状态、APNs消息内容、通知操作不同、iOS系统版本不同,回调方法也不同,下面这张图片是上次咨询个推时,个推的技术人员发我的,可以参考看看,注意使用时要测试一下,防止苹果系统又变化了:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-ced5adaebe3cb6f_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&500\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-ced5adaebe3cb6f_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E个推的透传消息可以通过方法(“GeTuiSdkDidReceivePayloadData: andTaskId: andMsgId: andOffLine:\nfromGtAppId: ”)回调获取,因为苹果的APNs推送不保证是否到达和到达时间,所以就可能会丢失,使用个推的透传方法相对APNs更能保证消息的到达率。\u003C\u002Fp\u003E\u003Cp\u003E
说到这里不得不说一下个推的推送机制了,在我们服务端给个推服务器推送消息时,个推服务器会检查推送对象是否在线(应该是根据个推SDK和个推服务器心跳包和网络连接判断的,超过一定时间没有收到心跳包就是“不在线”,不过这种做法可能会出现假在线情况,就是忽然断网,在服务器下次检测心跳包的期间,服务器会认为对象在线):\u003C\u002Fp\u003E\u003Cp\u003E
1)对象在线:下发个推的透传消息,不发送APNs推送消息。\u003C\u002Fp\u003E\u003Cp\u003E
2)对象离线:下发个推的透传消息,发送APNs推送消息。\u003C\u002Fp\u003E\u003Cp\u003E从上面可以看得出,个推的透传消息是每次都下发的,这样也保证的个推的消息到达率,不过这种做法会出现消息重复,例如是收到消息弹框提醒用户操作,个推透传消息和APNs推送消息都收到了,处理不好的话会提醒用户二次一样的消息。\u003C\u002Fp\u003E\u003Cp\u003E
这里比较好的是个推在透传消息方法中提供了“offLine”字段,这个值是“YES”时,表示这是一条离线消息,在下发个推透传消息时,也发送了APNs推送消息,在处理消息时可以忽略,如果消息的重要性不是很高,可以这么做,因为在忽略个推的透传消息后APNs消息也没有收到,就导致该条消息丢失。\u003C\u002Fp\u003E\u003Cp\u003E另外一种处理方式:参照网上的一些解决方法,我建立一个配置表,处理过的数据在表中标注,防止APNs和个推的透传方法消息重复操作。\u003C\u002Fp\u003E\u003Cp\u003E5、个推透传消息注意点\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-3fa70acfe_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&550\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-3fa70acfe_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图一\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-05c2fea401bcf9dfcaf2d7_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&29\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-05c2fea401bcf9dfcaf2d7_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图二\u003C\u002Fp\u003E\u003Cp\u003E
上面二张图,第一张是个推网站下发透传消息时的界面,第二张是个推透传消息回调方法。需要特别注意的是第一张图中最下面的“payload”和个推透传方法中“payloadData”,这二个中不是同一个概念。\u003C\u002Fp\u003E\u003Cp\u003E
“payload” 是个推自定义字段,添加在APNs的消息内容中,不是苹果原生字段,会通过APNs推送消息一并下发到iPhone客户端,结构如上图中代码块展示,这个字段一般是在APNs消息中添加附带消息,例如附带一个酒吧网站url,在收到通知消息是,发现是url,App直接打开这个网址。\u003C\u002Fp\u003E\u003Cp\u003E
“payloadData”是该条透传消息内容,对应图上的“*消息内容”,这个字段不会通过APNs推送到iPhone客户端,是通过个推服务器直接下发给个推SDK的。当然你也可以将“*消息内容”和“payload”设置成一样的,这个就看你们的具体使用情况来定了。\u003C\u002Fp\u003E\u003Cp\u003E
再说说第一张中“*拆分Android和iOS推送任务”,选择“是”的话,会拆分Android和iOS推送任务后,将生成两个taskid,分别对android和ios推送数据进行统计和展示,方便之后查询推送数据统计。\u003C\u002Fp\u003E\u003Cp\u003E
最后一个比较实用的就是个推的“高级通知”,如下图,将APNs推送中的字段都列举出来了,不要开发者特意记APNs中有哪些字段,方便一些对APNs还不是很熟悉的初学者使用,当然不包括我了,哈哈哈哈。\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-4244acb4d0c2f886e097_b.jpg\& data-rawwidth=\&550\& data-rawheight=\&644\& class=\&origin_image zh-lightbox-thumb\& width=\&550\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-4244acb4d0c2f886e097_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E6、发布到AppStore注意点\u003C\u002Fp\u003E\u003Cp\u003E
App发布到AppStore时,需要更换APNs证书或者更换App中个推AppId,因为个推的网站中只能上传一个证书,开发时上传的都是开发APNs证书,当开发测试完成后,准备发布时,App需要生产环境的APNs证书,这时有二种方案可以使用:\u003C\u002Fp\u003E\u003Cp\u003E
1)创建二套个推AppId:这种方案是在个推网站中添加二个应用,一个用于开发、一个用于发布,在开发测试期间使用开发的个推AppId,在发布时使用发布的个推AppId,这种方案需要注意发布时切换AppId,忘记换就GG,第一次发布还好,两个个推AppId的作用互换一下就可以了,如果是更新发布,那只能重新提交苹果审核了。\u003C\u002Fp\u003E\u003Cp\u003E
2)更换APNs证书:这种方案是在发布时重新上传生产APNs证书,注意个推的证书更换后需要10分钟左右生效,这种方案需要注意在之后版本更新开发时,需要申请新的个推AppId,不然会影响在线的客户。\u003C\u002Fp\u003E\u003Cp\u003E
我使用的是第一种方案,使用二套个推AppId,个推的文档中也是推荐使用第一种方案。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E三:公司服务器自己推送和使用个推推送的流程差异\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E1、公司服务器自己推送(简称:自己推送)流程\u003C\u002Fp\u003E\u003Cp\u003E
1)注册APNs,获取DeviceToken\u003C\u002Fp\u003E\u003Cp\u003E
2)将DeviceToken和用户ID绑定,保存服务器\u003C\u002Fp\u003E\u003Cp\u003E
3)推送时,根据用户ID获取到DeviceToken,将消息内容、DeviceToken和APNs推送证书发送给苹果服务器\u003C\u002Fp\u003E\u003Cp\u003E2、使用个推推送流程\u003C\u002Fp\u003E\u003Cp\u003E
1)注册APNs,获取DeviceToken\u003C\u002Fp\u003E\u003Cp\u003E
2)集成个推SDK,获取ClientId,绑定ClientId和DeviceToken\u003C\u002Fp\u003E\u003Cp\u003E
3)将ClientId和用户ID绑定,保存服务器\u003C\u002Fp\u003E\u003Cp\u003E
4)推送时,根据用户ID获取到ClientId,\n将消息内容和ClientId发送给个推服务器\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E四:自己推送和第三方对比\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E
1、成本:自己推送需要专人进行开发,并且需要一定数量的服务器和带宽支持,在开发完成后的使用过程中还需要有专人进行维护。使用第三方推送,只需要集成SDK就可以实现功能,不仅减小了开发成本与维护成本,甚至在推送稳定性上第三方也会比自己做的推送更好一些。\u003C\u002Fp\u003E\u003Cp\u003E
2、精准推送:可以将针对内容及标签等信息进行精准推送,比如将杭州的新闻推送给杭州用户,自己推送需要额外开发,而第三方大部分已经支持这样的功能。\u003C\u002Fp\u003E\u003Cp\u003E
3、推送统计:自己推送还是需要额外开发该功能,而第三方基本都必备该功能,相对来说就我现在使用的个推统计效果还是令人满意的,区分在线下发和APNs下发统计功能,支持通知的展示统计和点击统计,可以知道真实的下发量,下发后有多少被展示了,有多少被点击了。\u003C\u002Fp\u003E\u003Cp\u003E
4、可控性:使用第三方推送可控性太低,想想,如果第三方推送厂商宕机、或者被黑客攻击了,你服务没法推送了,需要等待第三方厂商响应,或者第三方厂商出问题了,也会影响你的推送。所以那些痛的经验告诉我们要选择家专业做推送,比如个推,至少人家也是百亿级用户量,服务器挂了怼他去,哈哈。\u003C\u002Fp\u003E\u003Cp\u003E
总结一下:自己推送成本高、服务相对更可控,使用第三方推送成本低、功能更多。建议如果公司特别大,对成本不在乎又要求服务可把控,可以自己搭建推送服务,如果是小公司或者才创业的公司,使用第三方厂商更加合适,没有统一答案,要根据自身产品特点、公司情况不断权衡和调整。 \u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E五:使用个推后的感受\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E
1、在开发测试时,更换了推送证书,证书更换后需要10分钟左右生效,测试时感觉好麻烦,不能立即生效么。\u003C\u002Fp\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
2、推送时,可以角标自动增加,产品的需求,作为一个开发人员不知道有什么好,不过产品这样要求,只能做了,还好个推支持。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-ccd0cec16fe4b672_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&172\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-ccd0cec16fe4b672_r.jpg\&\u003E\u003C\u002Ffigure\u003E3、可以统计通知的展示率和点击率,运营同学可以在推送活动通知后,知道用户对什么样的活动比较感兴趣,更方便他们运营。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-4ea410f34f02aa93de969_b.jpg\& data-rawwidth=\&644\& data-rawheight=\&339\& class=\&origin_image zh-lightbox-thumb\& width=\&644\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-4ea410f34f02aa93de969_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E4、可以对指定人群推送,例如我们活动在上海,可以指定给上海用户大力推送。这个比较好,不用全部用户都发送,保证不相关的用户不被打扰。\u003C\u002Fp\u003E\u003Cp\u003E
5、个推的透传方法可以保证数据的到达,因为苹果的APNs推送不保证是否到达和到达时间,所以就可能会丢失,使用个推的透传方法可以保证能收到消息。\u003C\u002Fp\u003E\u003Cp\u003E
6、在发送透传消息时,“iOS高级通知”中“代码块”功能比较赞,我个人超喜欢,可以提前预览客户端收到APNs通知消息的数据格式。\u003C\u002Fp\u003E&,&state&:&published&,&sourceUrl&:&&,&pageCommentsCount&:0,&canComment&:false,&snapshotUrl&:&&,&slug&:,&publishedTime&:&T15:53:14+08:00&,&url&:&\u002Fp\u002F&,&title&:&iOS APNs实战分享&,&summary&:&\u003Cb\u003E序言:\u003C\u002Fb\u003E 因为App的功能需要,最近一直在调研苹果的APNs推送,开始时觉得超麻烦,现在感觉还是比较easy,“难者不会,会者不难”,自己踩过了这么多的坑终于会了,不出来吐槽(装X)一下对不起自己,23333。 先给大家来一个小小福利,因为APNs只能在真机上测…&,&reviewingCommentsCount&:0,&meta&:{&previous&:null,&next&:null},&commentPermission&:&anyone&,&commentsCount&:0,&likesCount&:1},&next&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002F50\u002Fv2-2f2b8a832bdf3dc852392c_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&MySQL&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&高可用&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&数据库&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&推送\u002F大数据&,&isFollowing&:false,&hash&:&0032205cbb4bbaabbab233&,&uid&:583500,&isOrg&:false,&slug&:&hong-jack-49&,&isFollowed&:false,&description&:&&,&name&:&Hong Jack&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fhong-jack-49&,&avatar&:{&id&:&cf1fc371b328d477d726&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&column&:{&slug&:&getui&,&name&:&个推&},&content&:&\u003Cp\u003E数据库是所有应用系统的核心,故保证数据库稳定、高效、安全地运行是所有企业日常工作的重中之重。数据库系统一旦出现问题无法提供服务,有可能导致整个系统都无法继续工作。所以,一个成功的数据库架构在高可用设计方面也是需要充分考虑的。下面就为大家介绍一下如何构建一个高可用的MySQL数据库系统。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E做过DBA或者是运维的同学都应该知道,任何设备或服务,存在单点就会带来巨大风险,因为这台物理机一旦宕机或服务模块crash,若在短时间内无法找到替换的设备,势必会影响整个应用系统。因而如何保证不出现单点就是我们的重要工作,使用MySQL高可用方案可以很好地解决这个问题,一般有以下几种:\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E一、利用MySQL自身的Replication来实现高可用\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003EMySQL自带的Replication就是我们常说的主从复制(AB复制),通过对主服务器做一个从机,在主服务器宕机的情况下快速地将业务切换到从机上,保证应用的正常使用。利用AB复制做高可用方案也分为几种不同的架构:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E1、常规的MASTER---SLAVE解决方案\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E普通的MASTER---SLAVE是目前国内外大多数中小型公司最常用的一种架构方案,主要的好处就是简单、使用设备较少(成本较低)、维护方便。这种架构能解决单点的问题,而且还能在很大程度上解决系统的性能问题。在一个MASTER后面可以带上一个或者多个的SLAVE(主从级联复制),不过这种架构要求一个MASTER必须能够满足系统所有的写请求,不然就需要做水平拆分分担读的压力。\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-a87dcfecdcb42cea68b3f_b.jpg\& data-rawwidth=\&3131\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&3131\& data-original=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-a87dcfecdcb42cea68b3f_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图一\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-a07f8c602daaf2f5ade16e0e32d9dc44_b.jpg\& data-rawwidth=\&3128\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&3128\& data-original=\&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-a07f8c602daaf2f5ade16e0e32d9dc44_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图二\u003C\u002Fp\u003E\u003Cp\u003E图一到图二展示的是:解决单点问题和利用读写分离达到提高性能的过程。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E2、DUAL MASTER与级联复制结合\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E双主多从是在上面的方案中衍生而来的一种更加合理的方案。这个方案的好处是:当两个主服务器中任何一个挂掉时,整个架构都不用做大的调整。\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-dac82cd2a8c_b.jpg\& data-rawwidth=\&3177\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&3177\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-dac82cd2a8c_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图三\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-eea92596cdb212bcea55ec_b.jpg\& data-rawwidth=\&3205\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&3205\& data-original=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-eea92596cdb212bcea55ec_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图四\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-c0dfe222846a_b.jpg\& data-rawwidth=\&3072\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&3072\& data-original=\&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-c0dfe222846a_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图五\u003C\u002Fp\u003E\u003Cp\u003E这个过程如上图所示。但图五这种情况比较特殊,即MASTER-B宕机的话怎么办呢?首先可以确定的是我们的所有Write请求都不会受到任何影响,而且所有的Read请求也都能够正常访问;但所有Slave的复制都会中断,Slave上面的数据会开始出现滞后的现象。这时候我们需要做的就是将所有的Slave进行CHANGE MASTER TO操作,改为从Master A进行复制。由于所有Slave的复制都不可能超前最初的数据源,所以可以根据Slave上面的Relay Log中的时间戳信息与Master A中的时间戳信息进行对照,来找到准确的复制起始点,从而避免造成数据的丢失。\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E二、利用MYSQL CLUSTER实现整体的高可用\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E就目前而言,利用MYSQL CLUSTER实现整体的高可用(即NDB CLUSTER)的方案在国内的公司并没有很普及。NDB CLUSTER节点实际上就是一个多节点的MySQL服务器,但是并不包含数据,所以任何机器只要安装了就可以使用。当集群中某一个sql节点crash之后,因为节点不存具体的数据,所以数据不会丢失。如图六:\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-8eafabe0efbf39_b.jpg\& data-rawwidth=\&2720\& data-rawheight=\&2250\& class=\&origin_image zh-lightbox-thumb\& width=\&2720\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-8eafabe0efbf39_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图六\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E三、通过MySQL的衍生产品实现高可用\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E在目前MySQL实现高可用的衍生产品中,知名度的和普及度比较高的是GALERA CLUSTER和PERCONA XTRDB CLUSTER(PXC)。相关的内容本文暂不展开讲述,感兴趣的同学可以查阅相关资料进一步了解。这两种集群的实现方式都是类似的,如图七、图八:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-c00ec1e577ddc3bbc12c_b.jpg\& data-rawwidth=\&472\& data-rawheight=\&285\& class=\&origin_image zh-lightbox-thumb\& width=\&472\& data-original=\&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-c00ec1e577ddc3bbc12c_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图七\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cfigure\u003E\u003Cimg src=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-edf691eca932a1cf780c44_b.jpg\& data-rawwidth=\&730\& data-rawheight=\&438\& class=\&origin_image zh-lightbox-thumb\& width=\&730\& data-original=\&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-edf691eca932a1cf780c44_r.jpg\&\u003E\u003C\u002Ffigure\u003E\u003Cp\u003E图八\u003C\u002Fp\u003E\u003Ch2\u003E\u003Cb\u003E四、各种高可用方案的利弊比较\u003C\u002Fb\u003E\u003C\u002Fh2\u003E\u003Cp\u003E在前面各种高可用设计方案的介绍中读者们可能已经发现,不管是哪一种方案,都存在自己独特的优势,但也都或多或少存在一些限制。这一节将针对上面的几种主要方案做一个利弊分析,以供大家选择过程中参考。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E1、MySQL Replication\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E优势:部署简单,实施方便,维护也不复杂,是MySQL天生就支持的功能。且主备机之间切换方便,通过第三方软件或者自行编写的脚本即可自动完成主备切换。\u003C\u002Fp\u003E\u003Cp\u003E劣势:如果Master主机硬件故障且无法恢复,则可能造成部分未传送到Slave端的数据丢失。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E2、MySQL Cluster (NDB)\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E优势:可用性非常高,性能非常好。每一份数据至少在不同主机上面存在一份拷贝,且冗余数据拷贝实时同步。\u003C\u002Fp\u003E\u003Cp\u003E劣势:维护较为复杂,产品较新,存在部分bug,目前还不一定适用于比较核心的线上系统。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E3、GALERA CLUSTER和PERCONA XTRDB CLUSTER(PXC)\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E优势:可靠性非常高,所有节点可以同时读写每一份数据,至少在不同主机上面存在一份拷贝,且冗余数据拷贝实时同步。\u003C\u002Fp\u003E\u003Cp\u003E劣势:随着集群的规模扩大,性能会越来越差。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E4、 不得不提的DRBD磁盘网络镜像方案\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E从架构上来说,它有点类似Replication,只是它是通过第三方的软件实现数据同步的过程,可靠性比Replication更高,但是也牺牲了性能。\u003C\u002Fp\u003E\u003Cp\u003E优势:软件功能强大,数据在底层块设备级别跨物理主机镜像,且可根据性能和可靠性要求配置不同级别的同步。IO操作保持顺序,可满足数据库对数据一致性的苛刻要求。\u003C\u002Fp\u003E\u003Cp\u003E劣势:非分布式文件系统环境无法支持镜像数据同时可见,即性能和可靠性两者相互矛盾,无法适用于对二者要求都比较苛刻的环境。维护成本高于MySQL Replication。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E说完了各种常用架构的优缺点后,剩下的就是如何选择合适的架构在现实的生产环境中使用的问题。在这方面每个人都有自己的想法和经验,具体哪个方案是最优的就见仁见智了。在日常的工作中架构的完善并不是一蹴而就,而是一个不断演变优化完善的过程。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E个推在数据库方面也经历了从单点到主从再到主从+高可用的过程,同时也经历了从单一的MySQL+redis到MySQL+redis+es,最后到现在MySQL+redis+es+codis等等的演变。每一次的演变都是为了解决生产环境下的实际问题和痛点。单从MySQL来说任何一个架构都无法解决所有的问题(痛点),都需要根据实际的情况选择一个合适架构。MySQL集群实现的方案非常灵活多变,对于MySQL工作者来说如何选择一个合适的架构也是一种挑战,同时也是我们不断钻研和学习MySQL的动力。\u003C\u002Fp\u003E&,&state&:&published&,&sourceUrl&:&&,&pageCommentsCount&:0,&canComment&:false,&snapshotUrl&:&&,&slug&:,&publishedTime&:&T13:14:02+08:00&,&url&:&\u002Fp\u002F&,&title&:&开源数据库MySQL架构优劣对比&,&summary&:&数据库是所有应用系统的核心,故保证数据库稳定、高效、安全地运行是所有企业日常工作的重中之重。数据库系统一旦出现问题无法提供服务,有可能导致整个系统都无法继续工作。所以,一个成功的数据库架构在高可用设计方面也是需要充分考虑的。下面就为大家介…&,&reviewingCommentsCount&:0,&meta&:{&previous&:null,&next&:null},&commentPermission&:&anyone&,&commentsCount&:0,&likesCount&:0}},&annotationDetail&:null,&commentsCount&:0,&likesCount&:0,&FULLINFO&:true}},&User&:{&hong-jack-49&:{&isFollowed&:false,&name&:&Hong Jack&,&headline&:&&,&avatarUrl&:&https:\u002F\u002Fpic2.zhimg.com\u002Fcf1fc371b328d477d726_s.jpg&,&isFollowing&:false,&type&:&people&,&slug&:&hong-jack-49&,&bio&:&推送\u002F大数据&,&hash&:&0032205cbb4bbaabbab233&,&uid&:583500,&isOrg&:false,&description&:&&,&badge&:{&identity&:null,&bestAnswerer&:null},&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fhong-jack-49&,&avatar&:{&id&:&cf1fc371b328d477d726&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}},&Comment&:{},&favlists&:{}},&me&:{},&global&:{&experimentFeatures&:{&ge3&:&ge3_9&,&ge2&:&ge2_1&,&growthSearch&:&s2&,&sEI&:&c&,&nwebQAGrowth&:&experiment&,&qawebRelatedReadingsContentControl&:&close&,&liveStore&:&ls_a2_b2_c1_f2&,&nwebSearch&:&nweb_search_heifetz&,&rt&:&y&,&isOffice&:&false&,&enableTtsPlay&:&post&,&newLiveFeedMediacard&:&new&,&newMobileAppHeader&:&true&,&androidPassThroughPush&:&all&,&hybridZhmoreVideo&:&yes&,&nwebGrowthPeople&:&default&,&nwebSearchSuggest&:&default&,&qrcodeLogin&:&qrcode&,&enableVoteDownReasonMenu&:&enable&,&isShowUnicomFreeEntry&:&unicom_free_entry_off&,&newMobileColumnAppheader&:&new_header&,&androidDbRecommendAction&:&open&,&biu&:&1&,&androidDbFeedHashTagStyle&:&button&,&appStoreRateDialog&:&close&,&default&:&None&,&isNewNotiPanel&:&no&,&biua&:&1&,&zcmLighting&:&zcm&,&adR&:&b&,&wechatShareModal&:&wechat_share_modal_show&,&growthBanner&:&default&,&androidProfilePanel&:&panel_b&}},&columns&:{&next&:{},&getui&:{&following&:false,&canManage&:false,&href&:&\u002Fapi\u002Fcolumns\u002Fgetui&,&name&:&个推&,&creator&:{&slug&:&hong-jack-49&},&url&:&\u002Fgetui&,&slug&:&getui&,&avatar&:{&id&:&v2-ce535ef9cfde8f21ece0a&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&}}},&columnPosts&:{},&columnSettings&:{&colomnAuthor&:[],&uploadAvatarDetails&:&&,&contributeRequests&:[],&contributeRequestsTotalCount&:0,&inviteAuthor&:&&},&postComments&:{},&postReviewComments&:{&comments&:[],&newComments&:[],&hasMore&:true},&favlistsByUser&:{},&favlistRelations&:{},&promotions&:{},&switches&:{&couldSetPoster&:false},&draft&:{&titleImage&:&&,&titleImageSize&:{},&isTitleImageFullScreen&:false,&canTitleImageFullScreen&:false,&title&:&&,&titleImageUploading&:false,&error&:&&,&content&:&&,&draftLoading&:false,&globalLoading&:false,&pendingVideo&:{&resource&:null,&error&:null}},&drafts&:{&draftsList&:[],&next&:{}},&config&:{&userNotBindPhoneTipString&:{}},&recommendPosts&:{&articleRecommendations&:[],&columnRecommendations&:[]},&env&:{&edition&:{&baidu&:false,&yidianzixun&:false,&qqnews&:false},&isAppView&:false,&appViewConfig&:{&content_padding_top&:128,&content_padding_bottom&:56,&content_padding_left&:16,&content_padding_right&:16,&title_font_size&:22,&body_font_size&:16,&is_dark_theme&:false,&can_auto_load_image&:true,&app_info&:&OS=iOS&},&isApp&:false,&userAgent&:{&ua&:&Mozilla\u002F5.0 (compatible, MSIE 11, Windows NT 6.3; Trident\u002F7.0; rv:11.0) like Gecko&,&browser&:{&name&:&IE&,&version&:&11&,&major&:&11&},&engine&:{&version&:&7.0&,&name&:&Trident&},&os&:{&name&:&Windows&,&version&:&8.1&},&device&:{},&cpu&:{}}},&message&:{&newCount&:0},&pushNotification&:{&newCount&:0}}

我要回帖

更多关于 javascript 二维数组 的文章

 

随机推荐